예제 #1
0
        public static async Task Main(string[] args)
        {
            // Parse the command line arguments
            string lastArg = null, codeToExecute = null, socketPath = Defaults.FullSocketPath;
            bool   quiet = false;

            foreach (string arg in args)
            {
                if (lastArg == "-s" || lastArg == "--socket")
                {
                    socketPath = arg;
                }
                else if (lastArg == "-c" || lastArg == "-c")
                {
                    codeToExecute = arg;
                }
                else if (arg == "-q" || arg == "--quiet")
                {
                    quiet = true;
                }
                else if (arg == "-h" || arg == "--help")
                {
                    Console.WriteLine("Available command line arguments:");
                    Console.WriteLine("-s, --socket <socket>: UNIX socket to connect to");
                    Console.WriteLine("-c, --code <code>: Execute the given code(s), wait for the result and exit");
                    Console.WriteLine("-q, --quiet: Do not display when a connection has been established (only applicable in interactive mode)");
                    Console.WriteLine("-h, --help: Display this help text");
                    return;
                }
                lastArg = arg;
            }

            // Create a new connection and connect to DuetControlServer
            using CommandConnection connection = new CommandConnection();
            await connection.Connect(socketPath);

            // Check if this is an interactive session
            if (codeToExecute == null)
            {
                if (!quiet)
                {
                    // Notify the user that a connection has been established
                    Console.WriteLine("Connected!");
                }

                // Register an (interactive) user session
                int sessionId = await connection.AddUserSession(DuetAPI.Machine.AccessLevel.ReadWrite, DuetAPI.Machine.SessionType.Local, "console");

                // Start reading lines from stdin and send them to DCS as simple codes.
                // When the code has finished, the result is printed to stdout
                string input = Console.ReadLine();
                while (input != null && input != "exit" && input != "quit")
                {
                    try
                    {
                        string output = await connection.PerformSimpleCode(input);

                        if (output.EndsWith(Environment.NewLine))
                        {
                            Console.Write(output);
                        }
                        else
                        {
                            Console.WriteLine(output);
                        }
                    }
                    catch (SocketException)
                    {
                        Console.WriteLine("Server has closed the connection");
                        break;
                    }
                    catch (Exception e)
                    {
                        if (e is AggregateException ae)
                        {
                            e = ae.InnerException;
                        }
                        Console.WriteLine(e.Message);
                    }
                    input = Console.ReadLine();
                }

                // Unregister this session again
                if (connection.IsConnected)
                {
                    await connection.RemoveUserSession(sessionId);
                }
            }
            else
            {
                // Execute only the given code(s) and quit
                string output = await connection.PerformSimpleCode(codeToExecute);

                if (output.EndsWith('\n'))
                {
                    Console.Write(output);
                }
                else
                {
                    Console.WriteLine(output);
                }
            }
        }
        /// <summary>
        /// Deal with a newly opened WebSocket.
        /// A client may receive one of the WS codes: (1001) Endpoint unavailable (1003) Invalid command (1011) Internal error
        /// </summary>
        /// <param name="webSocket">WebSocket connection</param>
        /// <returns>Asynchronous task</returns>
        public async Task Process(WebSocket webSocket)
        {
            string socketPath = _configuration.GetValue("SocketPath", Defaults.FullSocketPath);

            // 1. Authentification. This will require an extra API command
            using CommandConnection commandConnection = new CommandConnection();
            // TODO

            // 2. Connect to DCS
            using SubscribeConnection subscribeConnection = new SubscribeConnection();
            try
            {
                // Subscribe to object model updates
                await subscribeConnection.Connect(SubscriptionMode.Patch, null, socketPath);
            }
            catch (AggregateException ae) when(ae.InnerException is IncompatibleVersionException)
            {
                _logger.LogError($"[{nameof(WebSocketController)}] Incompatible DCS version");
                await CloseConnection(webSocket, WebSocketCloseStatus.InternalServerError, "Incompatible DCS version");

                return;
            }
            catch (SocketException)
            {
                _logger.LogError($"[{nameof(WebSocketController)}] DCS is unavailable");
                await CloseConnection(webSocket, WebSocketCloseStatus.EndpointUnavailable, "DCS is unavailable");

                return;
            }

            // 3. Log this event
            string ipAddress = HttpContext.Connection.RemoteIpAddress.ToString();
            int    port      = HttpContext.Connection.RemotePort;

            _logger.LogInformation("WebSocket connected from {0}:{1}", ipAddress, port);

            // 4. Register this client and keep it up-to-date
            int sessionId = -1;

            try
            {
                // 4a. Register this user session. Once authentification has been implemented, the access level may vary
                await commandConnection.Connect(socketPath);

                sessionId = await commandConnection.AddUserSession(AccessLevel.ReadWrite, SessionType.HTTP, ipAddress, port);

                // 4b. Fetch full model copy and send it over initially
                using (MemoryStream json = await subscribeConnection.GetSerializedMachineModel())
                {
                    await webSocket.SendAsync(json.ToArray(), WebSocketMessageType.Text, true, default);
                }

                // 4c. Deal with this connection in full-duplex mode
                using CancellationTokenSource cts = new CancellationTokenSource();
                AsyncAutoResetEvent dataAcknowledged = new AsyncAutoResetEvent();
                Task rxTask = ReadFromClient(webSocket, dataAcknowledged, cts.Token);
                Task txTask = WriteToClient(webSocket, subscribeConnection, dataAcknowledged, cts.Token);

                // 4d. Deal with the tasks' lifecycles
                Task terminatedTask = await Task.WhenAny(rxTask, txTask);

                cts.Cancel();
                if (terminatedTask.IsFaulted)
                {
                    throw terminatedTask.Exception;
                }
            }
            catch (Exception e)
            {
                _logger.LogError(e, "WebSocket from {0}:{1} terminated with an exception", ipAddress, port);
                await CloseConnection(webSocket, WebSocketCloseStatus.InternalServerError, e.Message);
            }
            finally
            {
                _logger.LogInformation("WebSocket disconnected from {0}:{1}", ipAddress, port);
                try
                {
                    // Try to remove this user session again
                    await commandConnection.RemoveUserSession(sessionId);
                }
                catch (Exception e)
                {
                    if (!(e is SocketException))
                    {
                        _logger.LogError(e, "Failed to unregister user session");
                    }
                }
            }
        }