using Microsoft.Extensions.Logging;
|
using System.Text.RegularExpressions;
|
using WIDESEAWCS_S7Simulator.Application.Protocol;
|
using WIDESEAWCS_S7Simulator.Core.Entities;
|
using WIDESEAWCS_S7Simulator.Core.Enums;
|
using WIDESEAWCS_S7Simulator.Core.Interfaces;
|
using WIDESEAWCS_S7Simulator.Core.Protocol;
|
|
namespace WIDESEAWCS_S7Simulator.Application;
|
|
/// <summary>
|
/// 实例同步服务:协调数据库读取、协议模板生成、实例创建
|
/// </summary>
|
public class InstanceSyncService
|
{
|
private readonly DatabaseDeviceService _deviceService;
|
private readonly ISimulatorInstanceManager _instanceManager;
|
private readonly IProtocolTemplateService _templateService;
|
private readonly ILogger<InstanceSyncService> _logger;
|
|
private DateTime? _lastSyncTime;
|
|
public DateTime? LastSyncTime => _lastSyncTime;
|
|
public InstanceSyncService(
|
DatabaseDeviceService deviceService,
|
ISimulatorInstanceManager instanceManager,
|
IProtocolTemplateService templateService,
|
ILogger<InstanceSyncService> logger)
|
{
|
_deviceService = deviceService;
|
_instanceManager = instanceManager;
|
_templateService = templateService;
|
_logger = logger;
|
}
|
|
/// <summary>
|
/// 执行同步:从数据库读取设备,创建实例和协议模板
|
/// </summary>
|
public async Task SyncInstancesAsync()
|
{
|
_logger.LogInformation("开始同步实例...");
|
_lastSyncTime = DateTime.Now;
|
|
// 1. 获取所有 SiemensS7 设备
|
var devices = await _deviceService.GetSiemensS7DevicesAsync();
|
if (devices.Count == 0)
|
{
|
_logger.LogWarning("未找到 SiemensS7 设备,同步取消");
|
return;
|
}
|
|
// 2. 获取现有实例ID列表
|
var existingInstanceIds = _instanceManager.GetAllInstances().Select(i => i.Config.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
var dbDeviceCodes = devices.Select(d => d.DeviceCode).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
// 3. 清理已不存在的实例(数据库中没有但内存中有)
|
foreach (var instanceId in existingInstanceIds)
|
{
|
if (!dbDeviceCodes.Contains(instanceId))
|
{
|
_logger.LogInformation("数据库中已删除设备 {InstanceId},从内存移除", instanceId);
|
await _instanceManager.DeleteInstanceAsync(instanceId, deleteConfig: false);
|
}
|
}
|
|
// 4. 创建或更新实例
|
foreach (var device in devices)
|
{
|
try
|
{
|
// 4.1 获取设备协议
|
var protocols = await _deviceService.GetDeviceProtocolsAsync(device.Id);
|
|
// 预计算一个缓存字典:协议对象 -> DB编号
|
var dbCache = protocols.ToDictionary(
|
p => p,
|
p => int.TryParse(Regex.Match(p.DeviceProDataBlock, @"\d+").Value, out var n) ? n : 50
|
);
|
|
// 1. 获取唯一编号列表
|
var dbNumbers = dbCache.Values.Distinct().ToList();
|
|
// 4.2 创建协议模板
|
var templateId = $"protocol-{device.DeviceCode}";
|
var template = new ProtocolTemplate
|
{
|
Id = templateId,
|
Name = $"{device.DeviceName} 协议模板",
|
Version = "1.0",
|
Fields = protocols.Select(p => new ProtocolFieldMapping
|
{
|
FieldKey = $"{p.DeviceChildCode}_{p.DeviceProParamName}",
|
DbNumber = dbCache[p],
|
Offset = (int)p.DeviceProOffset,
|
Bit = 1,
|
DataType = MapDataType(p.DeviceProDataType),
|
Length = p.DeviceProDataLength,
|
Direction = ProtocolFieldDirection.Bidirectional
|
}).ToList()
|
};
|
await _templateService.UpsertAsync(template);
|
|
// 4.3 创建实例配置
|
var config = new InstanceConfig
|
{
|
Id = device.DeviceCode,
|
Name = device.DeviceName,
|
PLCType = SiemensPLCType.S71500,
|
Port = device.DevicePort,
|
AutoStart = false,
|
ProtocolTemplateId = templateId,
|
MemoryConfig = GetDefaultMemoryConfig(dbNumbers)
|
};
|
|
// 4.4 创建或更新实例
|
if (_instanceManager.InstanceExists(device.DeviceCode))
|
{
|
// 实例已存在,更新配置
|
var instance = _instanceManager.GetInstance(device.DeviceCode);
|
if (instance != null)
|
{
|
var oldState = instance.GetState();
|
var wasRunning = oldState.Status == InstanceStatus.Running;
|
|
// 停止旧实例
|
await _instanceManager.StopInstanceAsync(device.DeviceCode);
|
|
// 删除旧实例(保留配置)
|
await _instanceManager.DeleteInstanceAsync(device.DeviceCode, deleteConfig: false);
|
|
// 重新创建实例
|
var newInstance = await _instanceManager.CreateInstanceAsync(config);
|
if (wasRunning)
|
{
|
await _instanceManager.StartInstanceAsync(device.DeviceCode);
|
}
|
_logger.LogInformation("已更新实例 {DeviceCode} (端口:{Port})", device.DeviceCode, device.DevicePort);
|
}
|
}
|
else
|
{
|
await _instanceManager.CreateInstanceAsync(config);
|
_logger.LogInformation("已创建实例 {DeviceCode} (端口:{Port})", device.DeviceCode, device.DevicePort);
|
}
|
}
|
catch (Exception ex)
|
{
|
_logger.LogError(ex, "同步设备 {DeviceCode} 失败", device.DeviceCode);
|
}
|
}
|
|
_logger.LogInformation("同步完成");
|
}
|
|
/// <summary>
|
/// 数据类型映射:数据库字符串 -> ProtocolDataType 枚举
|
/// ProtocolDataType: Byte=0, Int=1, DInt=2, String=3, Bool=4
|
/// </summary>
|
private static ProtocolDataType MapDataType(string? dbDataType)
|
{
|
return dbDataType?.ToLowerInvariant() switch
|
{
|
"bit" => ProtocolDataType.Bool, // Bool=4
|
"byte" => ProtocolDataType.Byte, // Byte=0
|
"int" or "word" => ProtocolDataType.Int, // Int=1
|
"dint" => ProtocolDataType.DInt, // DInt=2
|
"string" or "string8" or "string16" => ProtocolDataType.String, // String=3
|
_ => ProtocolDataType.Byte
|
};
|
}
|
|
/// <summary>
|
/// 获取默认内存配置
|
/// </summary>
|
private static MemoryRegionConfig GetDefaultMemoryConfig(List<int> dbBlockNumbers)
|
{
|
return new MemoryRegionConfig
|
{
|
MRegionSize = 1024,
|
DBBlockCount = dbBlockNumbers.Count,
|
DBBlockNumbers = dbBlockNumbers,
|
DBBlockSize = 65536,
|
IRegionSize = 256,
|
QRegionSize = 256,
|
TRegionCount = 64,
|
CRegionCount = 64
|
};
|
}
|
}
|