using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using WIDESEAWCS_S7Simulator.Core.Enums;
using WIDESEAWCS_S7Simulator.Core.Interfaces;
using WIDESEAWCS_S7Simulator.Core.Protocol;
namespace WIDESEAWCS_S7Simulator.Application.Protocol;
///
/// Application 层的协议轮询服务。
/// 按配置编排模板与规则,并触发对应处理器执行。
///
public class ProtocolMonitoringHostedService : BackgroundService
{
private readonly ISimulatorInstanceManager _instanceManager;
private readonly IProtocolTemplateService _templateService;
private readonly Dictionary _protocolHandlers;
private readonly ILogger _logger;
private readonly ProtocolMonitoringOptions _options;
private readonly Dictionary _runtimeStates = new(StringComparer.OrdinalIgnoreCase);
public ProtocolMonitoringHostedService(
ISimulatorInstanceManager instanceManager,
IProtocolTemplateService templateService,
IOptions options,
IEnumerable protocolHandlers,
ILogger logger)
{
_instanceManager = instanceManager;
_templateService = templateService;
_protocolHandlers = protocolHandlers.ToDictionary(x => x.ProtocolName, StringComparer.OrdinalIgnoreCase);
_logger = logger;
_options = options.Value;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var intervalMs = Math.Max(50, _options.PollingIntervalMs);
while (!stoppingToken.IsCancellationRequested)
{
try
{
var bindings = _options.TemplateBindings;
foreach (var instance in _instanceManager.GetAllInstances())
{
if (instance.GetState().Status != InstanceStatus.Running)
{
continue;
}
var templateId = instance.Config.ProtocolTemplateId;
if (string.IsNullOrWhiteSpace(templateId))
{
continue;
}
var binding = bindings.FirstOrDefault(x => string.Equals(x.TemplateId, templateId, StringComparison.OrdinalIgnoreCase));
if (binding == null)
{
continue;
}
if (!_protocolHandlers.TryGetValue(binding.ProtocolName, out var protocolHandler))
{
continue;
}
var template = await _templateService.GetByIdAsync(templateId);
if (template == null)
{
continue;
}
if (!_runtimeStates.TryGetValue(instance.Config.Id, out var runtimeState))
{
runtimeState = new ProtocolRuntimeState();
_runtimeStates[instance.Config.Id] = runtimeState;
}
// 每轮先把实例当前内存同步到 MemoryStore,再执行规则处理。
var memoryData = instance.ExportMemory();
instance.MemoryStore.Import(memoryData);
var changed = protocolHandler.Process(instance.MemoryStore, template, runtimeState);
// 处理器有变更时,回写到 PLC 运行内存。
if (changed)
{
var updated = instance.MemoryStore.Export();
instance.ImportMemory(updated);
}
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Protocol monitoring loop failed.");
}
await Task.Delay(intervalMs, stoppingToken);
}
}
}