using System;
|
using System.Collections.Generic;
|
using System.IO;
|
using System.Linq;
|
using System.Text.Json;
|
using System.Threading.Tasks;
|
using WIDESEAWCS_S7Simulator.Core.Entities;
|
using WIDESEAWCS_S7Simulator.Core.Enums;
|
using WIDESEAWCS_S7Simulator.Core.Interfaces;
|
using WIDESEAWCS_S7Simulator.Core.Persistence.Models;
|
|
namespace WIDESEAWCS_S7Simulator.Core.Persistence
|
{
|
/// <summary>
|
/// 文件持久化服务实现
|
/// 将实例配置和内存数据保存到本地JSON文件
|
/// </summary>
|
public class FilePersistenceService : IPersistenceService
|
{
|
/// <summary>
|
/// 数据目录路径
|
/// </summary>
|
private readonly string _dataPath;
|
|
/// <summary>
|
/// JSON序列化选项
|
/// </summary>
|
private readonly JsonSerializerOptions _jsonOptions;
|
|
/// <summary>
|
/// 构造函数
|
/// </summary>
|
/// <param name="dataPath">数据目录路径</param>
|
public FilePersistenceService(string dataPath = "Data")
|
{
|
_dataPath = dataPath;
|
_jsonOptions = new JsonSerializerOptions
|
{
|
WriteIndented = true,
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
};
|
|
// 确保数据目录存在
|
if (!Directory.Exists(_dataPath))
|
{
|
Directory.CreateDirectory(_dataPath);
|
}
|
}
|
|
/// <summary>
|
/// 保存实例配置
|
/// </summary>
|
public async Task SaveInstanceConfigAsync(InstanceConfig config)
|
{
|
if (config == null)
|
throw new ArgumentNullException(nameof(config));
|
|
var instanceDir = GetInstanceDirectory(config.Id);
|
if (!Directory.Exists(instanceDir))
|
{
|
Directory.CreateDirectory(instanceDir);
|
}
|
|
var configPath = Path.Combine(instanceDir, "config.json");
|
var model = ToDataModel(config);
|
|
var json = JsonSerializer.Serialize(model, _jsonOptions);
|
await File.WriteAllTextAsync(configPath, json);
|
}
|
|
/// <summary>
|
/// 加载实例配置
|
/// </summary>
|
public async Task<InstanceConfig> LoadInstanceConfigAsync(string instanceId)
|
{
|
if (string.IsNullOrWhiteSpace(instanceId))
|
throw new ArgumentException("实例ID不能为空", nameof(instanceId));
|
|
var configPath = Path.Combine(GetInstanceDirectory(instanceId), "config.json");
|
if (!File.Exists(configPath))
|
throw new FileNotFoundException($"实例配置文件不存在: {configPath}");
|
|
var json = await File.ReadAllTextAsync(configPath);
|
var model = JsonSerializer.Deserialize<InstanceDataModel>(json, _jsonOptions);
|
|
if (model == null)
|
throw new InvalidOperationException("无法反序列化实例配置");
|
|
return ToEntity(model);
|
}
|
|
/// <summary>
|
/// 加载所有实例配置
|
/// </summary>
|
public async Task<List<InstanceConfig>> LoadAllInstanceConfigsAsync()
|
{
|
var configs = new List<InstanceConfig>();
|
|
if (!Directory.Exists(_dataPath))
|
return configs;
|
|
var instanceDirs = Directory.GetDirectories(_dataPath)
|
.Where(d => Path.GetFileName(d).StartsWith("instance-"))
|
.ToList();
|
|
foreach (var dir in instanceDirs)
|
{
|
var configPath = Path.Combine(dir, "config.json");
|
if (File.Exists(configPath))
|
{
|
try
|
{
|
var json = await File.ReadAllTextAsync(configPath);
|
var model = JsonSerializer.Deserialize<InstanceDataModel>(json, _jsonOptions);
|
if (model != null)
|
{
|
configs.Add(ToEntity(model));
|
}
|
}
|
catch (Exception)
|
{
|
// 跳过无法加载的配置文件
|
continue;
|
}
|
}
|
}
|
|
return configs;
|
}
|
|
/// <summary>
|
/// 删除实例配置
|
/// </summary>
|
public Task DeleteInstanceConfigAsync(string instanceId)
|
{
|
if (string.IsNullOrWhiteSpace(instanceId))
|
throw new ArgumentException("实例ID不能为空", nameof(instanceId));
|
|
var instanceDir = GetInstanceDirectory(instanceId);
|
if (Directory.Exists(instanceDir))
|
{
|
Directory.Delete(instanceDir, recursive: true);
|
}
|
|
return Task.CompletedTask;
|
}
|
|
/// <summary>
|
/// 保存内存数据
|
/// </summary>
|
public async Task SaveMemoryDataAsync(string instanceId, IMemoryStore memoryStore)
|
{
|
if (string.IsNullOrWhiteSpace(instanceId))
|
throw new ArgumentException("实例ID不能为空", nameof(instanceId));
|
|
if (memoryStore == null)
|
throw new ArgumentNullException(nameof(memoryStore));
|
|
var instanceDir = GetInstanceDirectory(instanceId);
|
if (!Directory.Exists(instanceDir))
|
{
|
Directory.CreateDirectory(instanceDir);
|
}
|
|
var memoryPath = Path.Combine(instanceDir, "memory.json");
|
var exportedData = memoryStore.Export();
|
|
// 将字节数组转换为Base64字符串以便JSON序列化
|
var memoryDataModel = new MemoryDataModel
|
{
|
MemoryData = exportedData.ToDictionary(
|
kvp => kvp.Key,
|
kvp => Convert.ToBase64String(kvp.Value)
|
)
|
};
|
|
var json = JsonSerializer.Serialize(memoryDataModel, _jsonOptions);
|
await File.WriteAllTextAsync(memoryPath, json);
|
}
|
|
/// <summary>
|
/// 加载内存数据
|
/// </summary>
|
public async Task LoadMemoryDataAsync(string instanceId, IMemoryStore memoryStore)
|
{
|
if (string.IsNullOrWhiteSpace(instanceId))
|
throw new ArgumentException("实例ID不能为空", nameof(instanceId));
|
|
if (memoryStore == null)
|
throw new ArgumentNullException(nameof(memoryStore));
|
|
var memoryPath = Path.Combine(GetInstanceDirectory(instanceId), "memory.json");
|
if (!File.Exists(memoryPath))
|
return; // 内存文件不存在,跳过加载
|
|
var json = await File.ReadAllTextAsync(memoryPath);
|
var memoryDataModel = JsonSerializer.Deserialize<MemoryDataModel>(json, _jsonOptions);
|
|
if (memoryDataModel?.MemoryData == null)
|
return;
|
|
// 将Base64字符串转换回字节数组
|
var importedData = new Dictionary<string, byte[]>();
|
foreach (var kvp in memoryDataModel.MemoryData)
|
{
|
try
|
{
|
importedData[kvp.Key] = Convert.FromBase64String(kvp.Value);
|
}
|
catch (FormatException)
|
{
|
// 跳过无效的Base64数据
|
continue;
|
}
|
}
|
|
memoryStore.Import(importedData);
|
}
|
|
/// <summary>
|
/// 获取实例目录路径
|
/// </summary>
|
private string GetInstanceDirectory(string instanceId)
|
{
|
return Path.Combine(_dataPath, $"instance-{instanceId}");
|
}
|
|
/// <summary>
|
/// 将实体转换为数据模型
|
/// </summary>
|
private InstanceDataModel ToDataModel(InstanceConfig config)
|
{
|
return new InstanceDataModel
|
{
|
Id = config.Id,
|
Name = config.Name,
|
PlcType = config.PLCType.ToString(),
|
Port = config.Port,
|
ActivationKey = config.ActivationKey,
|
AutoStart = config.AutoStart,
|
MemoryConfig = new MemoryRegionConfigModel
|
{
|
MRegionSize = config.MemoryConfig.MRegionSize,
|
DBBlockCount = config.MemoryConfig.DBBlockCount,
|
DBBlockSize = config.MemoryConfig.DBBlockSize,
|
IRegionSize = config.MemoryConfig.IRegionSize,
|
QRegionSize = config.MemoryConfig.QRegionSize,
|
TRegionCount = config.MemoryConfig.TRegionCount,
|
CRegionCount = config.MemoryConfig.CRegionCount
|
}
|
};
|
}
|
|
/// <summary>
|
/// 将数据模型转换为实体
|
/// </summary>
|
private InstanceConfig ToEntity(InstanceDataModel model)
|
{
|
return new InstanceConfig
|
{
|
Id = model.Id,
|
Name = model.Name,
|
PLCType = Enum.Parse<SiemensPLCType>(model.PlcType),
|
Port = model.Port,
|
ActivationKey = model.ActivationKey,
|
AutoStart = model.AutoStart,
|
MemoryConfig = new MemoryRegionConfig
|
{
|
MRegionSize = model.MemoryConfig.MRegionSize,
|
DBBlockCount = model.MemoryConfig.DBBlockCount,
|
DBBlockSize = model.MemoryConfig.DBBlockSize,
|
IRegionSize = model.MemoryConfig.IRegionSize,
|
QRegionSize = model.MemoryConfig.QRegionSize,
|
TRegionCount = model.MemoryConfig.TRegionCount,
|
CRegionCount = model.MemoryConfig.CRegionCount
|
}
|
};
|
}
|
}
|
}
|