For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 从 WCS 数据库自动同步设备到 S7 模拟器实例,启动时同步一次,也支持 API 手动触发
Architecture: 在 Application 层新增 DatabaseDeviceService 读取 WCS 数据库,InstanceSyncService 协调同步逻辑,SimulatorInstanceManager 创建/管理实例。协议模板通过现有 IProtocolTemplateService 保存到本地 JSON 文件。
Tech Stack: .NET 6, SqlSugarCore, ASP.NET Core, JSON 文件持久化
Application/
├── DTOs/
│ └── WcsDeviceDto.cs # 数据库字段映射 DTO
├── WcsDbOptions.cs # WCS 数据库配置
├── DatabaseDeviceService.cs # 读取 WCS 数据库
└── InstanceSyncService.cs # 同步协调逻辑
Server/
├── Controllers/
│ └── SyncController.cs # API 接口
├── appsettings.json # 添加 WcsDb 配置
└── Program.cs # 注册服务、启动同步
Files:
- Modify: WIDESEAWCS_S7Simulator.Application/WIDESEAWCS_S7Simulator.Application.csproj
<PackageReference Include="SqlSugarCore" Version="5.1.4.170" />
git add WIDESEAWCS_S7Simulator.Application/WIDESEAWCS_S7Simulator.Application.csproj
git commit -m "feat: 添加 SqlSugarCore 依赖用于数据库连接"
Files:
- Create: WIDESEAWCS_S7Simulator.Application/DTOs/WcsDeviceDto.cs
namespace WIDESEAWCS_S7Simulator.Application.DTOs;
/// <summary>
/// 设备信息 DTO(映射 WCS Dt_DeviceInfo 表)
/// </summary>
public class WcsDeviceDto
{
public int Id { get; set; }
public string DeviceCode { get; set; } = string.Empty;
public string DeviceName { get; set; } = string.Empty;
public string DeviceType { get; set; } = string.Empty;
public string DeviceStatus { get; set; } = string.Empty;
public string DeviceIp { get; set; } = string.Empty;
public int DevicePort { get; set; }
public string DevicePlcType { get; set; } = string.Empty;
public string DeviceRemark { get; set; } = string.Empty;
}
/// <summary>
/// 设备协议 DTO(映射 WCS Dt_DeviceProtocol 表)
/// </summary>
public class WcsDeviceProtocolDto
{
public int Id { get; set; }
public int DeviceId { get; set; }
public string DeviceChildCode { get; set; } = string.Empty;
public string DeviceProDataBlock { get; set; } = string.Empty;
public decimal DeviceProOffset { get; set; }
public string DeviceProDataType { get; set; } = string.Empty;
public int DeviceProDataLength { get; set; }
public string DeviceProParamName { get; set; } = string.Empty;
public string DeviceProParamType { get; set; } = string.Empty;
public string DeviceProParamDes { get; set; } = string.Empty;
public string DeviceProRemark { get; set; } = string.Empty;
}
git add WIDESEAWCS_S7Simulator.Application/DTOs/WcsDeviceDto.cs
git commit -m "feat: 添加 WCS 设备 DTO"
Files:
- Create: WIDESEAWCS_S7Simulator.Application/WcsDbOptions.cs
namespace WIDESEAWCS_S7Simulator.Application;
/// <summary>
/// WCS 数据库连接配置
/// </summary>
public class WcsDbOptions
{
public const string SectionName = "WcsDb";
/// <summary>
/// 是否启用数据库同步
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// 连接字符串
/// </summary>
public string ConnectionString { get; set; } = string.Empty;
/// <summary>
/// 数据库类型:SqlServer=2, MySql=1
/// </summary>
public int DbType { get; set; } = 2;
}
git add WIDESEAWCS_S7Simulator.Application/WcsDbOptions.cs
git commit -m "feat: 添加 WcsDbOptions 配置类"
Files:
- Create: WIDESEAWCS_S7Simulator.Application/DatabaseDeviceService.cs
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SqlSugar;
using WIDESEAWCS_S7Simulator.Application.DTOs;
namespace WIDESEAWCS_S7Simulator.Application;
/// <summary>
/// 读取 WCS 数据库设备数据
/// </summary>
public class DatabaseDeviceService : IDisposable
{
private readonly WcsDbOptions _options;
private readonly ILogger<DatabaseDeviceService> _logger;
private readonly SqlSugarScope _db;
public DatabaseDeviceService(IOptions<WcsDbOptions> options, ILogger<DatabaseDeviceService> logger)
{
_options = options.Value;
_logger = logger;
_db = new SqlSugarScope(new ConnectionConfig
{
ConnectionString = _options.ConnectionString,
DbType = (DbType)_options.DbType,
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute
});
}
/// <summary>
/// 获取所有 SiemensS7 类型的设备
/// </summary>
public async Task<List<WcsDeviceDto>> GetSiemensS7DevicesAsync()
{
try
{
var devices = await _db.Queryable<WcsDeviceDto>()
.Where(d => d.DevicePlcType == "SiemensS7")
.ToListAsync();
_logger.LogInformation("从数据库获取到 {Count} 个 SiemensS7 设备", devices.Count);
return devices;
}
catch (Exception ex)
{
_logger.LogError(ex, "从数据库获取设备失败");
return new List<WcsDeviceDto>();
}
}
/// <summary>
/// 获取指定设备的协议列表
/// </summary>
public async Task<List<WcsDeviceProtocolDto>> GetDeviceProtocolsAsync(int deviceId)
{
try
{
var protocols = await _db.Queryable<WcsDeviceProtocolDto>()
.Where(p => p.DeviceId == deviceId)
.ToListAsync();
return protocols;
}
catch (Exception ex)
{
_logger.LogError(ex, "获取设备协议失败 DeviceId={DeviceId}", deviceId);
return new List<WcsDeviceProtocolDto>();
}
}
public void Dispose()
{
_db.Dispose();
}
}
git add WIDESEAWCS_S7Simulator.Application/DatabaseDeviceService.cs
git commit -m "feat: 添加 DatabaseDeviceService 读取 WCS 数据库"
Files:
- Create: WIDESEAWCS_S7Simulator.Application/InstanceSyncService.cs
using Microsoft.Extensions.Logging;
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);
// 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.DeviceProParamName,
DbNumber = int.TryParse(p.DeviceProDataBlock, out var dbNum) ? dbNum : 50,
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, // 默认 S7-1500,可根据需要扩展
Port = device.DevicePort,
AutoStart = false,
ProtocolTemplateId = templateId,
MemoryConfig = GetDefaultMemoryConfig()
};
// 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()
{
return new MemoryRegionConfig
{
MRegionSize = 1024,
DBBlockCount = 1,
DBBlockNumbers = new List<int> { 50 },
DBBlockSize = 65536,
IRegionSize = 256,
QRegionSize = 256,
TRegionCount = 64,
CRegionCount = 64
};
}
}
git add WIDESEAWCS_S7Simulator.Application/InstanceSyncService.cs
git commit -m "feat: 添加 InstanceSyncService 同步协调逻辑"
Files:
- Create: WIDESEAWCS_S7Simulator.Server/Controllers/SyncController.cs
using Microsoft.AspNetCore.Mvc;
using WIDESEAWCS_S7Simulator.Application;
namespace WIDESEAWCS_S7Simulator.Server.Controllers;
[ApiController]
[Route("api/[controller]")]
public class SyncController : ControllerBase
{
private readonly InstanceSyncService _syncService;
private readonly ILogger<SyncController> _logger;
public SyncController(InstanceSyncService syncService, ILogger<SyncController> logger)
{
_syncService = syncService;
_logger = logger;
}
/// <summary>
/// 手动触发实例同步
/// </summary>
[HttpPost("SyncInstances")]
public async Task<IActionResult> SyncInstances()
{
try
{
_logger.LogInformation("收到手动同步请求");
await _syncService.SyncInstancesAsync();
return Ok(new { message = "同步完成", lastSyncTime = _syncService.LastSyncTime });
}
catch (Exception ex)
{
_logger.LogError(ex, "手动同步失败");
return StatusCode(500, new { message = "同步失败", error = ex.Message });
}
}
/// <summary>
/// 获取上次同步时间
/// </summary>
[HttpGet("LastSyncTime")]
public IActionResult GetLastSyncTime()
{
return Ok(new { lastSyncTime = _syncService.LastSyncTime });
}
}
git add WIDESEAWCS_S7Simulator.Server/Controllers/SyncController.cs
git commit -m "feat: 添加 SyncController API 接口"
Files:
- Modify: WIDESEAWCS_S7Simulator.Server/appsettings.json
在 appsettings.json 根对象添加:
"WcsDb": {
"Enabled": true,
"ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWCS_ShanMei;User ID=sa;Password=${WCS_DB_PASSWORD};Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
"DbType": 2
}
注意:${WCS_DB_PASSWORD} 为环境变量占位符,实际部署时替换为真实密码或通过环境变量注入。
git add WIDESEAWCS_S7Simulator.Server/appsettings.json
git commit -m "feat: 添加 WcsDb 数据库配置"
Files:
- Modify: WIDESEAWCS_S7Simulator.Server/Program.cs
在 builder.Services.AddControllers() 之后添加:
// WCS 数据库配置
builder.Services.Configure<WcsDbOptions>(builder.Configuration.GetSection(WcsDbOptions.SectionName));
builder.Services.AddSingleton<DatabaseDeviceService>();
builder.Services.AddSingleton<InstanceSyncService>();
在 app.MapControllers() 之后,将同步整合到现有的 Task.Run 中:
// 启动时加载已保存的实例(不自动启动)
var instanceManager = app.Services.GetRequiredService<ISimulatorInstanceManager>();
var syncService = app.Services.GetRequiredService<InstanceSyncService>();
_ = Task.Run(async () =>
{
try
{
// 先加载已保存的实例
await instanceManager.LoadSavedInstancesAsync(autoStart: false);
Console.WriteLine($"Loaded {instanceManager.GetAllInstances().Count()} saved instances.");
// 如果启用了 WCS 数据库同步,则执行同步
var wcsDbOptions = app.Services.GetRequiredService<IOptions<WcsDbOptions>>().Value;
if (wcsDbOptions.Enabled)
{
await syncService.SyncInstancesAsync();
Console.WriteLine($"WCS DB sync completed. Last sync: {syncService.LastSyncTime}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error during startup: {ex.Message}");
}
});
git add WIDESEAWCS_S7Simulator.Server/Program.cs
git commit -m "feat: Program.cs 注册同步服务并启动时同步"
cd WIDESEAWCS_S7Simulator.Server
dotnet build
预期:无编译错误