using GeViScopeBridge.Services; using GeViScopeBridge.SDK; using Serilog; using Microsoft.Extensions.Configuration; namespace GeViScopeBridge { public class Program { public static async Task Main(string[] args) { // Configure Serilog Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console() .WriteTo.File("logs/sdk-bridge-.log", rollingInterval: RollingInterval.Day) .CreateLogger(); try { Log.Information("Starting GeViSoft/GeViScope SDK Bridge (gRPC Server)"); // Load configuration var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false) .AddEnvironmentVariables() .Build(); // Get GeViScope connection settings (for video operations) string geviScopeHost = configuration["GeViScope:Host"] ?? "localhost"; string geviScopeUsername = configuration["GeViScope:Username"] ?? "sysadmin"; string geviScopePassword = configuration["GeViScope:Password"] ?? ""; // Get GeViSoft connection settings (for configuration/action mapping) string geviSoftHost = configuration["GeViSoft:Host"] ?? "localhost"; string geviSoftUsername = configuration["GeViSoft:Username"] ?? "sysadmin"; string geviSoftPassword = configuration["GeViSoft:Password"] ?? ""; int grpcPort = int.Parse(configuration["GrpcServer:Port"] ?? "50051"); Log.Information("Configuration loaded: GeViScope={ScopeHost}, GeViSoft={SoftHost}, gRPC Port={Port}", geviScopeHost, geviSoftHost, grpcPort); // Create GeViScope SDK wrapper (for video operations) var geviScopeWrapper = new GeViDatabaseWrapper( geviScopeHost, geviScopeUsername, geviScopePassword, Log.Logger); // Create GeViSoft SDK wrapper (for configuration management) var geviSoftWrapper = new GeViDatabaseWrapper( geviSoftHost, geviSoftUsername, geviSoftPassword, Log.Logger); // Connect to GeViSoft (GeViServer) - PRIMARY connection Log.Information("Connecting to GeViSoft (GeViServer)..."); bool softConnected = await geviSoftWrapper.ConnectAsync(); if (!softConnected) { Log.Fatal("Failed to connect to GeViSoft. Exiting."); return; } Log.Information("Successfully connected to GeViSoft"); // Connect to GeViScope (GSCServer) - OPTIONAL for video operations Log.Information("Connecting to GeViScope (GSCServer)..."); bool scopeConnected = await geviScopeWrapper.ConnectAsync(); if (scopeConnected) { Log.Information("Successfully connected to GeViScope"); } else { Log.Warning("Failed to connect to GeViScope - video operations will be unavailable"); } // Create SDK handlers (using GeViSoft for state queries and actions) var stateQueryHandler = new StateQueryHandler(geviSoftWrapper, Log.Logger); var actionDispatcher = new ActionDispatcher(geviSoftWrapper, Log.Logger); // Create action mapping handler (using GeViSoft for configuration) var actionMappingHandler = new ActionMappingHandler(geviSoftWrapper, stateQueryHandler, Log.Logger); if (softConnected) { await actionMappingHandler.InitializeAsync(); } // Create SetupClient wrapper for configuration operations var setupClient = new GeViSetupClientWrapper(geviSoftHost, geviSoftUsername, geviSoftPassword); bool setupConnected = await setupClient.ConnectAsync(); if (setupConnected) { Log.Information("SetupClient connected for configuration operations"); // DIAGNOSTIC: Log all marker names on startup try { Log.Information("DIAGNOSTIC: Reading configuration to identify marker names..."); var config = setupClient.ReadAndParseConfiguration(); if (config != null) { var allMarkers = config.RootNodes.Where(n => n.NodeType == "marker").ToList(); var markerNames = allMarkers.Select(n => n.Name).Distinct().OrderBy(n => n).ToList(); Log.Information("DIAGNOSTIC: Found {MarkerCount} markers with {UniqueCount} unique names:", allMarkers.Count, markerNames.Count); foreach (var name in markerNames) { var count = allMarkers.Count(m => m.Name == name); Log.Information(" - '{Name}': {Count} occurrences", name, count); } } } catch (Exception ex) { Log.Warning(ex, "DIAGNOSTIC: Failed to read marker names"); } } else { Log.Warning("SetupClient connection failed - configuration operations will be unavailable"); } // Build gRPC server var builder = WebApplication.CreateBuilder(args); // Add gRPC services builder.Services.AddGrpc(); // Add singletons - GeViScope for existing operations builder.Services.AddSingleton(geviScopeWrapper); builder.Services.AddSingleton(provider => geviScopeWrapper); builder.Services.AddSingleton(stateQueryHandler); builder.Services.AddSingleton(actionDispatcher); builder.Services.AddSingleton(Log.Logger); // Add GeViSoft singletons for action mapping operations builder.Services.AddSingleton(geviSoftWrapper); builder.Services.AddSingleton(actionMappingHandler); // Add SetupClient for configuration operations builder.Services.AddSingleton(setupClient); // Configure Kestrel for gRPC builder.WebHost.ConfigureKestrel(options => { options.ListenAnyIP(grpcPort, listenOptions => { listenOptions.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http2; }); }); var app = builder.Build(); // Map gRPC services app.MapGrpcService(); app.MapGrpcService(); app.MapGrpcService(); app.MapGrpcService(); app.MapGrpcService(); // Add health check endpoint app.MapGet("/", () => "GeViScope SDK Bridge (gRPC) - Use a gRPC client to connect"); Log.Information("gRPC server starting on port {Port}", grpcPort); Log.Information("Services registered: CameraService, MonitorService, CrossSwitchService, ActionMappingService, ConfigurationService"); await app.RunAsync(); } catch (Exception ex) { Log.Fatal(ex, "Application terminated unexpectedly"); } finally { Log.Information("Shutting down GeViScope SDK Bridge"); Log.CloseAndFlush(); } } } }