Files
I150/srcs/RobotNet10/RobotApp/RobotNet10.RobotApp/Xloc/XlocAutoLocalizationHostedService.cs
2026-07-03 16:37:12 +07:00

111 lines
4.1 KiB
C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace RobotNet10.RobotApp.Xloc;
/// <summary>
/// Polls XLOC diagnostics and calls <see cref="XlocIntegrationService.StartLocalization"/> when
/// state transitions to READY (3) with an active map. Does not depend on the web UI.
/// </summary>
public sealed class XlocAutoLocalizationHostedService : BackgroundService
{
private readonly XlocIntegrationService _xloc;
private readonly ILogger<XlocAutoLocalizationHostedService> _logger;
private readonly XlocIntegrationConfiguration _config = new();
private byte? _previousXlocState;
private bool _hasAutoStartedOnce;
public XlocAutoLocalizationHostedService(
XlocIntegrationService xloc,
IConfiguration configuration,
ILogger<XlocAutoLocalizationHostedService> logger)
{
_xloc = xloc;
_logger = logger;
var section = configuration.GetSection("Xloc:Integration");
if (section.Exists())
section.Bind(_config);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
if (!_config.Enabled)
{
_logger.LogInformation("XlocAutoLocalizationHostedService skipped: Xloc:Integration:Enabled is false");
return;
}
if (!_config.AutoStartLocalizationOnReady)
{
_logger.LogInformation("XlocAutoLocalizationHostedService skipped: AutoStartLocalizationOnReady is false");
return;
}
var pollMs = Math.Clamp(_config.AutoStartLocalizationPollIntervalMs, 100, 10_000);
var cooldownMs = Math.Max(0, _config.AutoStartLocalizationCooldownAfterStopMs);
_logger.LogInformation(
"XlocAutoLocalizationHostedService running (poll {PollMs}ms, stop cooldown {CooldownMs}ms)",
pollMs,
cooldownMs);
while (!stoppingToken.IsCancellationRequested)
{
try
{
await Task.Delay(pollMs, stoppingToken).ConfigureAwait(false);
var diag = _xloc.GetDiagnostics();
if (diag == null)
continue;
var s = diag.XlocState;
var transitionedToReady = s == 3 && (!_previousXlocState.HasValue || _previousXlocState.Value != 3);
_previousXlocState = s;
if (!transitionedToReady || _hasAutoStartedOnce)
continue;
if (string.IsNullOrWhiteSpace(diag.CurrentActiveMap))
{
_logger.LogDebug("Auto-start localization skipped: no active map in diagnostics");
continue;
}
if (_xloc.LastLocalizationStopUtc.HasValue)
{
var elapsedMs = (DateTime.UtcNow - _xloc.LastLocalizationStopUtc.Value).TotalMilliseconds;
if (elapsedMs < cooldownMs)
{
_logger.LogDebug(
"Auto-start localization skipped: cooldown after stop ({ElapsedMs:F0}ms < {CooldownMs}ms)",
elapsedMs,
cooldownMs);
continue;
}
}
_hasAutoStartedOnce = true;
_logger.LogInformation(
"XLOC READY with active map '{Map}'. Auto-starting localization...",
diag.CurrentActiveMap);
var ok = _xloc.StartLocalization();
if (!ok)
_logger.LogWarning("Auto-start localization failed (StartLocalization returned false)");
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in XlocAutoLocalizationHostedService loop");
}
}
}
}