wanshenmean
8 天以前 559bb7b4be83575cc5fedee98484647243c96f89
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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))
                {
                    _logger.LogInformation("实例 {DeviceCode} 已存在,跳过创建", device.DeviceCode);
                }
                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
        };
    }
}