private static async Task Main(string[] args) { // Parse the command line arguments string lastArg = null, socketPath = Defaults.FullSocketPath, filter = null; bool quiet = false; foreach (string arg in args) { if (lastArg == "-s" || lastArg == "--socket") { socketPath = arg; } else if (lastArg == "-f" || lastArg == "--filter") { filter = 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("-f, --filter <filter>: UNIX socket to connect to"); Console.WriteLine("-q, --quiet: Do not display when a connection has been established"); Console.WriteLine("-h, --help: Display this help text"); return; } lastArg = arg; } // Get an optional filter string if (string.IsNullOrWhiteSpace(filter)) { Console.WriteLine("Please enter a filter expression or press RETURN to receive partial model updates:"); filter = Console.ReadLine().Trim(); } // Connect to DCS using SubscribeConnection connection = new SubscribeConnection(); #pragma warning disable CS0612 // Type or member is obsolete await connection.Connect(SubscriptionMode.Patch, filter, socketPath); #pragma warning restore CS0612 // Type or member is obsolete if (!quiet) { Console.WriteLine("Connected!"); } // Write incoming fragments indented to the console do { try { using JsonDocument patch = await connection.GetObjectModelPatch(); Console.WriteLine(GetIndentedJson(patch)); } catch (SocketException) { if (!quiet) { Console.WriteLine("Server has closed the connection"); } break; } }while (true); }
/// <summary> /// Synchronize all registered endpoints and user sessions /// </summary> public async Task Execute() { string unixSocket = _configuration.GetValue("SocketPath", DuetAPI.Connection.Defaults.FullSocketPath); int retryDelay = _configuration.GetValue("ModelRetryDelay", 5000); ObjectModel model; try { do { try { // Establish connections to DCS using SubscribeConnection subscribeConnection = new SubscribeConnection(); using CommandConnection commandConnection = new CommandConnection(); await subscribeConnection.Connect(DuetAPI.Connection.SubscriptionMode.Patch, new string[] { "directories/www", "httpEndpoints/**", "network/corsSite", "userSessions/**" }, unixSocket); await commandConnection.Connect(unixSocket); _logger.LogInformation("Connections to DuetControlServer established"); // Get the machine model and keep it up-to-date model = await subscribeConnection.GetObjectModel(_stopRequest.Token); if (!string.IsNullOrEmpty(model.Network.CorsSite)) { _logger.LogInformation("Changing CORS policy to accept site '{0}'", model.Network.CorsSite); CorsPolicy.Origins.Add(model.Network.CorsSite); } lock (Endpoints) { Endpoints.Clear(); foreach (HttpEndpoint ep in model.HttpEndpoints) { string fullPath = (ep.Namespace == HttpEndpoint.RepRapFirmwareNamespace) ? $"{ep.EndpointType}/rr_{ep.Path}" : $"{ep.EndpointType}/machine/{ep.Namespace}/{ep.Path}"; Endpoints[fullPath] = ep; _logger.LogInformation("Registered HTTP endpoint {0}", fullPath); } } // Keep track of the web directory _commandConnection = commandConnection; model.Directories.PropertyChanged += Directories_PropertyChanged; string wwwDirectory = await commandConnection.ResolvePath(model.Directories.Web); OnWebDirectoryChanged?.Invoke(wwwDirectory); do { // Wait for more updates using JsonDocument jsonPatch = await subscribeConnection.GetObjectModelPatch(_stopRequest.Token); model.UpdateFromJson(jsonPatch.RootElement); // Check for updated CORS site if (jsonPatch.RootElement.TryGetProperty("network", out _)) { CorsPolicy.Origins.Clear(); if (!string.IsNullOrEmpty(model.Network.CorsSite)) { _logger.LogInformation("Changing CORS policy to accept site '{0}'", model.Network.CorsSite); CorsPolicy.Origins.Add(model.Network.CorsSite); } else { _logger.LogInformation("Reset CORS policy"); } } // Check if the HTTP sessions have changed and rebuild them on demand if (jsonPatch.RootElement.TryGetProperty("httpEndpoints", out _)) { _logger.LogInformation("New number of custom HTTP endpoints: {0}", model.HttpEndpoints.Count); lock (Endpoints) { Endpoints.Clear(); foreach (HttpEndpoint ep in model.HttpEndpoints) { string fullPath = $"{ep.EndpointType}/machine/{ep.Namespace}/{ep.Path}"; Endpoints[fullPath] = ep; _logger.LogInformation("Registered HTTP {0} endpoint via /machine/{1}/{2}", ep.EndpointType, ep.Namespace, ep.Path); } } } // Rebuild the list of user sessions on demand if (jsonPatch.RootElement.TryGetProperty("userSessions", out _)) { lock (UserSessions) { UserSessions.Clear(); foreach (UserSession session in model.UserSessions) { UserSessions[session.Origin] = session.Id; } } } }while (!_stopRequest.IsCancellationRequested); } catch (Exception e) when(!(e is OperationCanceledException)) { _logger.LogWarning(e, "Failed to synchronize machine model"); await Task.Delay(retryDelay, _stopRequest.Token); } }while (!_stopRequest.IsCancellationRequested); } catch (OperationCanceledException) { // unhandled } }