namespace ZB.MOM.WW.ScadaBridge.DelmiaNotifier; internal static class Program { public static async Task Main(string[] args) { var stdout = Console.Out; // Config first, so the diagnostic log can honour the configured LogPath even for early failures. NotifierConfig cfg; try { cfg = ConfigLoader.LoadFromDefaultFile(); } catch (Exception ex) { new DiagLog(null).Write("config load failed: " + ex.Message); return Reporter.Report(false, "config load failed: " + ex.Message, stdout); } var log = new DiagLog(cfg.ScadaBridge.LogPath); try { var parse = ArgParser.Parse(args); if (!parse.Ok) { log.Write("arg error: " + parse.Error); return Reporter.Report(false, parse.Error!, stdout); } var baseUrls = ConfigLoader.SplitBaseUrls(cfg.ScadaBridge.BaseUrls); if (baseUrls.Length == 0) { log.Write("no BaseUrls configured"); return Reporter.Report(false, "no BaseUrls configured", stdout); } var apiKey = ConfigLoader.ResolveApiKey(Environment.GetEnvironmentVariable); if (apiKey is null) { log.Write("API key not configured (SCADABRIDGE_API_KEY)"); return Reporter.Report(false, "API key not configured (SCADABRIDGE_API_KEY)", stdout); } using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(cfg.ScadaBridge.TimeoutSeconds) }; var sender = new LoggingRecipeSender(new HttpRecipeSender(http, apiKey), log); log.Write($"notifying {baseUrls.Length} URL(s) for machine {parse.Payload!.MachineCode}"); var result = await Notifier.RunAsync(baseUrls, parse.Payload, sender, CancellationToken.None); log.Write($"result: ok={result.Ok} reason={result.Reason}"); return Reporter.Report(result.Ok, result.Reason, stdout); } catch (Exception ex) { log.Write("unexpected error: " + ex); return Reporter.Report(false, "unexpected error: " + ex.Message, stdout); } } /// Decorates a sender to emit a per-attempt diagnostic line; keeps stdout reserved for the YES/NO contract. private sealed class LoggingRecipeSender(IRecipeSender inner, DiagLog log) : IRecipeSender { public async Task SendAsync(string baseUrl, RecipeDownload payload, CancellationToken ct) { var outcome = await inner.SendAsync(baseUrl, payload, ct); log.Write(outcome.Kind == AttemptKind.ConnectFailed ? $"attempt {baseUrl}: connect failed: {outcome.Error}" : $"attempt {baseUrl}: HTTP {outcome.StatusCode}"); return outcome; } } }