Files
scadalink-design/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/Program.cs
Joseph Doherty 73fe618953 fix(lmxproxy): protect probe subscription from ReadAsync teardown, add instance configs
ReadAsync internally subscribes/unsubscribes the same ScanTime tag used
by the persistent probe, which was tearing down the probe subscription
and triggering false reconnects every ~5s. Guard UnsubscribeInternal and
stored subscription state so the probe tag is never removed by other
callers. Also removes DetailedHealthCheckService (redundant with the
persistent probe), adds per-instance config files (appsettings.v2.json,
appsettings.v2b.json) loaded via LMXPROXY_INSTANCE env var so deploys
no longer overwrite port settings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:20:05 -04:00

84 lines
3.2 KiB
C#

using System;
using Microsoft.Extensions.Configuration;
using Serilog;
using Topshelf;
using ZB.MOM.WW.LmxProxy.Host.Configuration;
namespace ZB.MOM.WW.LmxProxy.Host
{
internal static class Program
{
static int Main(string[] args)
{
// 1. Build configuration (instance override file loaded from LMXPROXY_INSTANCE env var)
var instance = Environment.GetEnvironmentVariable("LMXPROXY_INSTANCE");
var configuration = new ConfigurationBuilder()
.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
.AddJsonFile($"appsettings.{instance}.json", optional: true, reloadOnChange: false)
.AddEnvironmentVariables()
.Build();
// 2. Set working directory to exe location so relative log paths resolve correctly
Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
// 3. Configure Serilog
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.Enrich.WithThreadId()
.CreateLogger();
try
{
// 4. Bind configuration
var config = new LmxProxyConfiguration();
configuration.Bind(config);
// 5. Configure Topshelf
var exitCode = HostFactory.Run(host =>
{
host.UseSerilog();
host.Service<LmxProxyService>(service =>
{
service.ConstructUsing(() => new LmxProxyService(config));
service.WhenStarted(s => s.Start());
service.WhenStopped(s => s.Stop());
service.WhenPaused(s => s.Pause());
service.WhenContinued(s => s.Continue());
service.WhenShutdown(s => s.Stop());
});
host.SetServiceName("ZB.MOM.WW.LmxProxy.Host");
host.SetDisplayName("SCADA Bridge LMX Proxy");
host.SetDescription("gRPC proxy for AVEVA System Platform via MXAccess COM API");
host.StartAutomatically();
host.EnablePauseAndContinue();
host.EnableServiceRecovery(recovery =>
{
recovery.RestartService(config.ServiceRecovery.FirstFailureDelayMinutes);
recovery.RestartService(config.ServiceRecovery.SecondFailureDelayMinutes);
recovery.RestartService(config.ServiceRecovery.SubsequentFailureDelayMinutes);
recovery.SetResetPeriod(config.ServiceRecovery.ResetPeriodDays);
});
});
return (int)exitCode;
}
catch (Exception ex)
{
Log.Fatal(ex, "LmxProxy service terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
}
}