using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using TeamAAS_VP.Core;
using TeamAAS_VP.Interfaces;
using TeamAAS_VP.Models;
using TeamAAS_VP.Models.Calibration;
using TeamAAS_VP.Models.Feeder;
using TeamAAS_VP.Models.Lights;
using TeamAAS_VP.Models.PLC;
namespace TeamAAS_VP.Services
{
///
/// 配置参数服务实现类。
/// 线程安全:内部使用私有锁字段 来保护对内存集合的并发访问。
/// 持久化:对集合的修改会写回到对应的配置文件(路径由内部静态类 定义)。
///
public class ConfigService : IConfigService
{
private static class ConfigPaths
{
// 将路径集中管理于本类,当前使用现有 FilePath 值以保持兼容
public static readonly string CamerasConfigurationPath = "..//Config//CameraConfiguration.cfg";
public static readonly string RobotsConfigurationPath = "..//Config//RobotsConfiguration.cfg";
public static readonly string FeedersConfigurationPath = "..//Config//FeedersConfiguration.cfg";
public static readonly string PlcConfigurationPath = "..//Config//PlcConfiguration.cfg";
public static readonly string BgTcpIpConfigurationPath = "..//Config//BgTcpIpConfiguration.cfg";
public static readonly string BgModbusTcpConfigurationPath = "..//Config//BgModbusTcpConfiguration.cfg";
public static readonly string SystemConfigurationPath = "..//Config//SystemConfiguration.cfg";
// Feeder清料任务配置路径
public static readonly string FeederClearanceTasksConfigurationPath = "..//Config//FeederClearanceTasksConfiguration.cfg";
// 光源控制器配置路径
public static readonly string LightControllersConfigurationPath = "..//Config//LightControllersConfiguration.cfg";
}
private readonly object _sync = new object();
private ObservableCollection Cameras { get; set; }
private ObservableCollection Robots { get; set; }
private ObservableCollection Feeders { get; set; }
private ObservableCollection Plcs { get; set; }
private BgTcpIP BgCommunicate { get; set; }
private BgModbusTcp BgModbusCommunicate { get; set; }
private ObservableCollection FeederClearanceTasks { get; set; }
// 光源控制器配置
private ObservableCollection LightControllers { get; set; }
private PlcAddressConfig PlcAddressConfig { get; set; }
///
/// 初始化一个新的 实例。
/// 构造函数不会自动加载配置文件(避免在构造期间进行 I/O 操作);可调用 或 显式加载。
///
public ConfigService()
{
//LoadAll();
}
///
/// 同步加载所有配置文件至内存(若对应文件不存在,将创建默认空集合/对象并写入文件)。
/// 线程安全:方法内部对共享资源使用 加锁。
///
public void LoadAll()
{
lock (_sync)
{
// 读取各配置,如果文件不存在,则保持 DeviceConfig 默认值
if (File.Exists(ConfigPaths.CamerasConfigurationPath))
Cameras = FileHelper.ReadJsonFile>(ConfigPaths.CamerasConfigurationPath);
else
{
Cameras = new ObservableCollection();
FileHelper.WriteJsonFile(Cameras, ConfigPaths.CamerasConfigurationPath);
}
if (File.Exists(ConfigPaths.RobotsConfigurationPath))
Robots = FileHelper.ReadJsonFile>(ConfigPaths.RobotsConfigurationPath);
else
{
Robots = new ObservableCollection();
FileHelper.WriteJsonFile(Robots, ConfigPaths.RobotsConfigurationPath);
}
if (File.Exists(ConfigPaths.FeedersConfigurationPath))
Feeders = FileHelper.ReadJsonFile>(ConfigPaths.FeedersConfigurationPath);
else
{
Feeders = new ObservableCollection();
FileHelper.WriteJsonFile(Feeders, ConfigPaths.FeedersConfigurationPath);
}
if (File.Exists(ConfigPaths.PlcConfigurationPath))
Plcs = FileHelper.ReadJsonFile>(ConfigPaths.PlcConfigurationPath);
else
{
Plcs = new ObservableCollection();
FileHelper.WriteJsonFile(Plcs, ConfigPaths.PlcConfigurationPath);
}
if (File.Exists(ConfigPaths.BgTcpIpConfigurationPath))
BgCommunicate = FileHelper.ReadJsonFile(ConfigPaths.BgTcpIpConfigurationPath);
else
{
BgCommunicate = new BgTcpIP();
FileHelper.WriteJsonFile(BgCommunicate, ConfigPaths.BgTcpIpConfigurationPath);
}
if (File.Exists(ConfigPaths.BgModbusTcpConfigurationPath))
BgModbusCommunicate = FileHelper.ReadJsonFile(ConfigPaths.BgModbusTcpConfigurationPath);
else
{
BgModbusCommunicate = new BgModbusTcp();
FileHelper.WriteJsonFile(BgModbusCommunicate, ConfigPaths.BgModbusTcpConfigurationPath);
}
if (!File.Exists(ConfigPaths.FeederClearanceTasksConfigurationPath))
{
FeederClearanceTasks = new ObservableCollection();
FileHelper.WriteJsonFile(FeederClearanceTasks, ConfigPaths.FeederClearanceTasksConfigurationPath);
}
else
{
// 读取 Feeder清料任务 配置
FeederClearanceTasks = FileHelper.ReadJsonFile>(ConfigPaths.FeederClearanceTasksConfigurationPath);
}
// 读取光源控制器配置
if (!File.Exists(ConfigPaths.LightControllersConfigurationPath))
{
LightControllers = new ObservableCollection();
FileHelper.WriteJsonFile(LightControllers, ConfigPaths.LightControllersConfigurationPath);
}
else
{
LightControllers = FileHelper.ReadJsonFile>(ConfigPaths.LightControllersConfigurationPath);
}
// load plc addresses
var plcAddressPath = "..//Config//PlcAddressConfiguration.cfg";
if (File.Exists(plcAddressPath))
PlcAddressConfig = FileHelper.ReadJsonFile(plcAddressPath);
else
{
PlcAddressConfig = new PlcAddressConfig();
PlcAddressConfig.SetDefaultValue();
FileHelper.WriteJsonFile(PlcAddressConfig, plcAddressPath);
}
}
}
///
/// 异步执行 操作。
///
/// 表示异步加载操作的任务。
public Task LoadAllAsync()
{
return Task.Run(() => LoadAll());
}
///
/// 将内存中的所有配置写回到对应配置文件。
/// 线程安全:方法内部对写入操作使用 加锁。
///
public void SaveAll()
{
lock (_sync)
{
FileHelper.WriteJsonFile(Cameras, ConfigPaths.CamerasConfigurationPath);
FileHelper.WriteJsonFile(Robots, ConfigPaths.RobotsConfigurationPath);
FileHelper.WriteJsonFile(Feeders, ConfigPaths.FeedersConfigurationPath);
FileHelper.WriteJsonFile(Plcs, ConfigPaths.PlcConfigurationPath);
FileHelper.WriteJsonFile(BgCommunicate, ConfigPaths.BgTcpIpConfigurationPath);
FileHelper.WriteJsonFile(BgModbusCommunicate, ConfigPaths.BgModbusTcpConfigurationPath);
FileHelper.WriteJsonFile(LightControllers, ConfigPaths.LightControllersConfigurationPath);
// save plc addresses
//FileHelper.WriteJsonFile(PlcAddressConfig, "..//Config//PlcAddressConfiguration.cfg");
}
}
///
/// 异步执行 操作。
///
/// 表示异步保存操作的任务。
public Task SaveAllAsync()
{
return Task.Run(() => SaveAll());
}
#region Cameras
///
/// 获取所有相机配置的只读集合。
/// 线程安全:在内部加锁以保证并发读取的一致性。
///
/// 相机配置的只读集合。
public IReadOnlyCollection GetAllCameras()
{
lock (_sync) { return Cameras.ToList().AsReadOnly(); }
}
///
/// 根据标识获取单个相机配置信息。
///
/// 相机的 Guid 标识。
/// 找到则返回对应的 ,否则返回 null。
public CameraInfo GetCamera(Guid id)
{
lock (_sync) { return Cameras.FirstOrDefault(c => c.Id == id); }
}
///
/// 新增或更新相机配置。
/// - 若传入 为 null 返回 null。
/// - 若 Id 已存在则替换;否则追加并设置连续的 CameraNo 编号。
/// 方法结束后会将相机集合持久化到配置文件。
///
/// 要添加或更新的相机配置。
/// 已添加或更新的 ;输入为 null 时返回 null。
public CameraInfo AddOrUpdateCamera(CameraInfo camera)
{
if (camera == null) return null;
lock (_sync)
{
var exist = Cameras.FirstOrDefault(c => c.Id == camera.Id);
if (exist != null)
{
var idx = Cameras.IndexOf(exist);
Cameras[idx] = camera;
}
else
{
camera.CameraNo = Cameras.Count + 1;
Cameras.Add(camera);
}
}
FileHelper.WriteJsonFile(Cameras, ConfigPaths.CamerasConfigurationPath);
return camera;
}
///
/// 根据标识移除相机配置并重新编号剩余相机的 CameraNo。
/// 方法结束后会将相机集合持久化到配置文件。
///
/// 要移除的相机 Guid。
/// 若找到并成功移除返回 true,否则返回 false。
public bool RemoveCamera(Guid id)
{
bool result = false;
lock (_sync)
{
var exist = Cameras.FirstOrDefault(c => c.Id == id);
if (exist != null) result = Cameras.Remove(exist);
//重新编号
int no = 1;
//对所有相机按CameraNo排序
var sortedCameras = Cameras.OrderBy(c => c.CameraNo).ToList();
foreach (var cam in sortedCameras)
{
cam.CameraNo = no;
no++;
}
Cameras.Clear();
foreach (var cam in sortedCameras)
{
Cameras.Add(cam);
}
}
FileHelper.WriteJsonFile(Cameras, ConfigPaths.CamerasConfigurationPath);
return result;
}
///
/// 检查是否包含指定 Id 的相机配置。
///
/// 相机的 Guid。
/// 存在返回 true,否则返回 false。
public bool ContainsCamera(Guid id) { lock (_sync) { return Cameras.Any(c => c.Id == id); } }
#endregion
#region Robots
///
/// 获取所有机器人配置的只读集合。
///
/// 机器人配置的只读集合。
public IReadOnlyCollection GetAllRobots() { lock (_sync) { return Robots.ToList().AsReadOnly(); } }
///
/// 根据标识获取单个机器人配置。
///
/// 机器人的 Guid 标识。
/// 找到则返回对应的 ,否则返回 null。
public RobotInfo GetRobot(Guid id) { lock (_sync) { return Robots.FirstOrDefault(r => r.Id == id); } }
///
/// 新增或更新机器人配置,操作后持久化至配置文件。
/// 新增时为机器人分配连续的 RobotNo 编号。
///
/// 要添加或更新的机器人配置。
/// 已添加或更新的 ;输入为 null 时返回 null。
public RobotInfo AddOrUpdateRobot(RobotInfo robot)
{
if (robot == null) return null;
lock (_sync)
{
var exist = Robots.FirstOrDefault(r => r.Id == robot.Id);
if (exist != null)
{
var idx = Robots.IndexOf(exist);
Robots[idx] = robot;
}
else
{
robot.RobotNo = Robots.Count + 1;
Robots.Add(robot);
}
}
FileHelper.WriteJsonFile(Robots, ConfigPaths.RobotsConfigurationPath);
return robot;
}
///
/// 根据标识移除机器人配置并重新编号剩余机器人。
/// 操作后持久化配置文件。
///
/// 要移除的机器人 Guid。
/// 移除成功返回 true,否则返回 false。
public bool RemoveRobot(Guid id)
{
bool result = false;
lock (_sync)
{
var e = Robots.FirstOrDefault(r => r.Id == id);
if (e != null)
result = Robots.Remove(e);
//重新编号
int no = 1;
//对所有机器人按RobotNo排序
var sortedRobots = Robots.OrderBy(r => r.RobotNo).ToList();
foreach (var r in sortedRobots)
{
r.RobotNo = no;
no++;
}
Robots.Clear();
foreach (var r in sortedRobots)
{
Robots.Add(r);
}
}
FileHelper.WriteJsonFile(Robots, ConfigPaths.RobotsConfigurationPath);
return result;
}
///
/// 判断是否包含指定 Id 的机器人配置。
///
/// 机器人的 Guid。
/// 存在返回 true,否则返回 false。
public bool ContainsRobot(Guid id) { lock (_sync) { return Robots.Any(r => r.Id == id); } }
///
/// 修改指定机器人 Id的步进距离并持久化机器人配置。
///
/// 机器人唯一标识符。
/// 步进距离。
/// 更新成功返回 true,找不到机器人返回 false。
public bool UpdateRobotStepDistance(Guid id, double stepDistance)
{
lock (_sync)
{
var robot = Robots.FirstOrDefault(r => r.Id == id);
if (robot != null)
{
robot.StepDistance = stepDistance;
FileHelper.WriteJsonFile(Robots, ConfigPaths.RobotsConfigurationPath);
return true;
}
else
{
return false;
}
}
}
///
/// 获取指定机器人 Id 的步进距离。
///
/// 机器人 Guid。
/// 若找到则返回对应的步进距离,否则返回默认值 1.0。
public double GetRobotStepDistance(Guid id)
{
lock (_sync)
{
var robot = Robots.FirstOrDefault(r => r.Id == id);
if (robot != null)
{
return robot.StepDistance;
}
else
{
return 1.0;
}
}
}
#endregion
#region Feeders
///
/// 获取所有供料器配置的只读集合。
///
/// 供料器配置的只读集合。
public IReadOnlyCollection GetAllFeeders() { lock (_sync) { return Feeders.ToList().AsReadOnly(); } }
///
/// 根据标识获取单个供料器配置。
///
/// 供料器的 Guid 标识。
/// 找到则返回对应的 ,否则返回 null。
public FeederInfo GetFeeder(Guid id) { lock (_sync) { return Feeders.FirstOrDefault(f => f.Id == id); } }
///
/// 新增或更新供料器配置,操作后持久化至配置文件。
/// 新增时为供料器分配连续的 FeederNo 编号。
///
/// 要添加或更新的供料器配置。
/// 已添加或更新的 ;输入为 null 时返回 null。
public FeederInfo AddOrUpdateFeeder(FeederInfo feeder)
{
if (feeder == null) return null;
lock (_sync)
{
var exist = Feeders.FirstOrDefault(f => f.Id == feeder.Id);
if (exist != null)
{
var idx = Feeders.IndexOf(exist);
Feeders[idx] = feeder;
}
else
{
feeder.FeederNo = Feeders.Count + 1;
Feeders.Add(feeder);
}
}
FileHelper.WriteJsonFile(Feeders, ConfigPaths.FeedersConfigurationPath);
return feeder;
}
///
/// 根据标识移除供料器配置并重新编号剩余供料器。
/// 操作后持久化配置文件。
///
/// 要移除的供料器 Guid。
/// 移除成功返回 true,否则返回 false。
public bool RemoveFeeder(Guid id)
{
bool result = false;
lock (_sync)
{
var e = Feeders.FirstOrDefault(f => f.Id == id);
if (e != null) result = Feeders.Remove(e);
//重新编号
int no = 1;
//对所有供料器按FeederNo排序
var sortedFeeders = Feeders.OrderBy(f => f.FeederNo).ToList();
foreach (var f in sortedFeeders)
{
f.FeederNo = no;
no++;
}
Feeders.Clear();
foreach (var f in sortedFeeders)
{
Feeders.Add(f);
}
}
FileHelper.WriteJsonFile(Feeders, ConfigPaths.FeedersConfigurationPath);
return result;
}
///
/// 判断是否包含指定 Id 的供料器配置。
///
/// 供料器 Guid。
/// 存在返回 true,否则返回 false。
public bool ContainsFeeder(Guid id) { lock (_sync) { return Feeders.Any(f => f.Id == id); } }
#endregion
#region PLC
///
/// 获取所有 PLC 配置的只读集合。
///
/// PLC 配置的只读集合。
public IReadOnlyCollection GetAllPlcs() { lock (_sync) { return Plcs.ToList().AsReadOnly(); } }
///
/// 根据标识获取单个 PLC 配置。
///
/// PLC 的 Guid 标识。
/// 匹配的 实例,找不到返回 null。
public PlcInfo GetPlc(Guid id) { lock (_sync) { return Plcs.FirstOrDefault(p => p.Id == id); } }
///
/// 新增或更新 PLC 配置,操作后持久化至配置文件;新增时分配连续 PlcNo 编号。
///
/// 要添加或更新的 PLC 配置。
/// 已添加或更新的 ;输入为 null 时返回 null。
public PlcInfo AddOrUpdatePlc(PlcInfo plc)
{
if (plc == null) return null;
lock (_sync)
{
var exist = Plcs.FirstOrDefault(p => p.Id == plc.Id);
if (exist != null)
{
var idx = Plcs.IndexOf(exist);
Plcs[idx] = plc;
}
else
{
plc.PlcNo = Plcs.Count + 1;
Plcs.Add(plc);
}
}
FileHelper.WriteJsonFile(Plcs, ConfigPaths.PlcConfigurationPath);
return plc;
}
///
/// 根据标识移除 PLC 配置并重新编号剩余 PLC。
/// 操作后持久化配置文件。
///
/// 要移除的 PLC Guid。
/// 移除成功返回 true,否则返回 false。
public bool RemovePlc(Guid id)
{
bool result = false;
lock (_sync)
{
var e = Plcs.FirstOrDefault(p => p.Id == id);
if (e != null) result = Plcs.Remove(e);
//重新编号
int no = 1;
//对所有PLC按PlcNo排序
var sortedPlcs = Plcs.OrderBy(p => p.PlcNo).ToList();
foreach (var p in sortedPlcs)
{
p.PlcNo = no;
no++;
}
Plcs.Clear();
foreach (var p in sortedPlcs)
{
Plcs.Add(p);
}
}
FileHelper.WriteJsonFile(Plcs, ConfigPaths.PlcConfigurationPath);
return result;
}
///
/// 判断是否包含指定 Id 的 PLC 配置。
///
/// PLC 的 Guid。
/// 存在返回 true,否则返回 false。
public bool ContainsPlc(Guid id) { lock (_sync) { return Plcs.Any(p => p.Id == id); } }
#endregion
#region BgTcp
///
/// 获取 BG TCP/IP 通信配置(内存对象,可能为 null)。
///
/// 当前的 配置对象。
public BgTcpIP GetBgTcp() { lock (_sync) { return BgCommunicate; } }
///
/// 设置 BG TCP/IP 通信配置(仅更新内存对象,不会自动持久化)。
/// 若需持久化请调用 。
///
/// 新的 配置对象。
public void SetBgTcp(BgTcpIP cfg) { lock (_sync) { BgCommunicate = cfg; } }
///
/// 将当前内存中的 BG TCP/IP 配置持久化到配置文件。
///
public void SaveBgTcp() { lock (_sync) { FileHelper.WriteJsonFile(BgCommunicate, ConfigPaths.BgTcpIpConfigurationPath); } }
#endregion
#region BgModbus
///
/// 获取 BG Modbus TCP 通信配置(内存对象)。
///
/// 当前的 配置对象。
public BgModbusTcp GetBgModbusTcp() { lock (_sync) { return BgModbusCommunicate; } }
///
/// 设置 BG Modbus TCP 通信配置(仅更新内存对象,不会自动持久化)。
///
/// 新的 配置对象。
public void SetBgModbusTcp(BgModbusTcp cfg) { lock (_sync) { BgModbusCommunicate = cfg; } }
///
/// 将当前内存中的 BG Modbus TCP 配置持久化到配置文件。
///
public void SaveBgModbusTcp() { lock (_sync) { FileHelper.WriteJsonFile(BgModbusCommunicate, ConfigPaths.BgModbusTcpConfigurationPath); } }
#endregion
#region 系统参数
///
/// 获取系统设置(从配置文件读取)。
/// 线程安全:方法内部加锁以保证读取一致性。
///
/// 读取到的 对象;若文件不存在则返回默认的新实例。
public SystemConfiguration GetSystemConfiguration()
{
lock (_sync)
{
var config = new SystemConfiguration();
if (File.Exists(ConfigPaths.SystemConfigurationPath))
{
config = FileHelper.ReadJsonFile(ConfigPaths.SystemConfigurationPath);
}
return config;
}
}
///
/// 保存系统设置到配置文件(会覆盖现有文件)。
///
/// 要保存的系统配置对象。
public void SaveSystemConfiguration(SystemConfiguration systemConfiguration)
{
lock (_sync)
{
FileHelper.WriteJsonFile(systemConfiguration, ConfigPaths.SystemConfigurationPath);
}
}
#endregion
#region Feeder清料任务
///
/// 获取所有清料任务的只读集合。
///
/// 清料任务的只读集合。
public IReadOnlyCollection GetAllClearanceTasks() { lock (_sync) { return FeederClearanceTasks.ToList().AsReadOnly(); } }
///
/// 新增或更新清料任务,新增时为任务分配递增的 TaskCode,完成后持久化配置文件。
///
/// 要添加或更新的清料任务对象。
/// 已添加或更新的 ;输入为 null 时返回 null。
public FeederClearWork AddOrUpdateClearanceTask(FeederClearWork task)
{
if (task == null) return null;
lock (_sync)
{
var exist = FeederClearanceTasks.FirstOrDefault(t => t.Id == task.Id);
if (exist != null)
{
var idx = FeederClearanceTasks.IndexOf(exist);
FeederClearanceTasks[idx] = task;
}
else
{
// 设置任务编号为当前最大编号加1
task.TaskCode = FeederClearanceTasks.Count > 0 ? FeederClearanceTasks.Max(t => t.TaskCode) + 1 : 1;
FeederClearanceTasks.Add(task);
}
}
FileHelper.WriteJsonFile(FeederClearanceTasks, ConfigPaths.FeederClearanceTasksConfigurationPath);
return task;
}
///
/// 根据标识移除清料任务并持久化配置文件。
///
/// 要移除的清料任务 Guid。
/// 若找到并移除返回 true,否则返回 false。
public bool RemoveClearanceTask(Guid id)
{
lock (_sync)
{
var exist = FeederClearanceTasks.FirstOrDefault(t => t.Id == id);
if (exist != null)
{
FileHelper.WriteJsonFile(FeederClearanceTasks, ConfigPaths.FeederClearanceTasksConfigurationPath);
return FeederClearanceTasks.Remove(exist);
}
else
return false;
}
}
///
/// 清除所有清料任务并持久化空集合到配置文件。
///
public void ClearAllClearanceTasks()
{
lock (_sync)
{
FeederClearanceTasks.Clear();
}
FileHelper.WriteJsonFile(FeederClearanceTasks, ConfigPaths.FeederClearanceTasksConfigurationPath);
}
///
/// 判断是否包含指定 Id 的清料任务。
///
/// 清料任务 Guid。
/// 存在返回 true,否则返回 false。
public bool ContainsClearanceTask(Guid id)
{
lock (_sync) { return FeederClearanceTasks.Any(t => t.Id == id); }
}
///
/// 根据标识获取单个清料任务信息。
///
/// 清料任务 Guid。
/// 匹配的 ,找不到返回 null。
public FeederClearWork GetClearanceTask(Guid id)
{
lock (_sync) { return FeederClearanceTasks.FirstOrDefault(t => t.Id == id); }
}
///
/// 判断是否包含指定任务编号的清料任务。
///
/// 任务编号(TaskCode)。
/// 存在返回 true,否则返回 false。
public bool ContainsClearanceTaskByNumber(int taskNumber)
{
lock (_sync) { return FeederClearanceTasks.Any(t => t.TaskCode == taskNumber); }
}
///
/// 根据任务编号获取单个清料任务信息。
///
/// 任务编号(TaskCode)。
/// 匹配的 ,找不到返回 null。
public FeederClearWork GetClearanceTaskByNumber(int taskNumber)
{
lock (_sync) { return FeederClearanceTasks.FirstOrDefault(t => t.TaskCode == taskNumber); }
}
#endregion
#region Light controllers and channels
///
/// 获取所有光源控制器配置的只读集合。
/// 线程安全:在内部使用 进行锁定以保证并发访问安全。
///
/// 按当前内存中存储顺序返回的只读 集合。
public IReadOnlyCollection GetAllLightControllers()
{
lock (_sync) { return LightControllers.ToList().AsReadOnly(); }
}
///
/// 根据控制器标识获取单个光源控制器配置。
/// 线程安全:该方法对内存集合进行只读访问并在内部加锁。
///
/// 控制器的整数标识(Id)。
/// 匹配的 实例,找不到则返回 null。
public LightControllerConfig GetLightController(int id)
{
lock (_sync) { return LightControllers.FirstOrDefault(c => c.Id == id); }
}
///
/// 新增或更新光源控制器配置。
/// - 若 为 null 返回 null。
/// - 更新时以 Id 匹配现有项并替换。
/// - 新增时分配连续的控制器 Id,并为控制器内的每个通道分配全局通道编号(GlobalIndex)。
/// 线程安全:在内部使用 锁定集合。
///
/// 要添加或更新的 实例。
/// 已添加或更新的 实例;输入为 null 时返回 null。
/// 当要添加/更新的控制器名称与现有其他控制器重复时抛出。
public LightControllerConfig AddOrUpdateLightController(LightControllerConfig controller)
{
if (controller == null) return null;
lock (_sync)
{
// 名称不得与其他控制器重复(同名但不同 Id 不允许)
if (LightControllers.Any(c => c.Name == controller.Name && c.Id != controller.Id))
throw new ArgumentException("Controller name must be unique");
var exist = LightControllers.FirstOrDefault(c => c.Id == controller.Id);
if (exist != null)
{
// 更新现有控制器(按索引替换)
var idx = LightControllers.IndexOf(exist);
LightControllers[idx] = controller;
}
else
{
// 新增控制器
// 分配控制器编号为当前最大 Id + 1(若集合为空则为 1)
controller.Id = LightControllers.Count > 0 ? LightControllers.Max(c => c.Id) + 1 : 1;
// 计算下一个全局通道索引:在所有已存在控制器的通道 GlobalIndex 上取最大值并加1
int nextGlobal = 0;
if (LightControllers.SelectMany(c => c.ChannelConfigs).Any())
{
nextGlobal = LightControllers.SelectMany(c => c.ChannelConfigs).Max(ch => ch.GlobalIndex) + 1;
}
// 遍历新增控制器的通道,若通道的 GlobalIndex 无效(<0)或与现有通道冲突,则重新分配
for (int i = 0; i < controller.ChannelConfigs.Count; i++)
{
var ch = controller.ChannelConfigs[i];
if (ch.GlobalIndex < 0 || LightControllers.SelectMany(c => c.ChannelConfigs).Any(existingCh => existingCh.GlobalIndex == ch.GlobalIndex))
{
ch.GlobalIndex = nextGlobal;
nextGlobal++;
}
}
LightControllers.Add(controller);
}
}
// 持久化更新后的光源控制器配置
FileHelper.WriteJsonFile(LightControllers, ConfigPaths.LightControllersConfigurationPath);
return controller;
}
///
/// 根据 Id 移除光源控制器配置并对剩余控制器重新编号(从 1 开始连续编号)。
/// 操作后持久化配置文件。
///
/// 要移除的控制器 Id。
/// 如果成功移除返回 true,否则返回 false(比如未找到该 Id)。
public bool RemoveLightController(int id)
{
bool result = false;
lock (_sync)
{
var e = LightControllers.FirstOrDefault(c => c.Id == id);
if (e != null)
result = LightControllers.Remove(e);
// 重新按 Id 排序并从 1 开始重新编号,以保持连续性
int no = 1;
var sorted = LightControllers.OrderBy(c => c.Id).ToList();
foreach (var c in sorted)
{
c.Id = no;
no++;
}
LightControllers.Clear();
foreach (var c in sorted)
{
LightControllers.Add(c);
}
}
FileHelper.WriteJsonFile(LightControllers, ConfigPaths.LightControllersConfigurationPath);
return result;
}
///
/// 判断是否包含指定 Id 的光源控制器。
///
/// 控制器 Id。
/// 存在返回 true,否则返回 false。
public bool ContainsLightController(int id) { lock (_sync) { return LightControllers.Any(c => c.Id == id); } }
///
/// 获取所有光源通道的只读集合,按 GlobalIndex 升序返回。
///
/// 按 GlobalIndex 排序的 只读集合。
public IReadOnlyCollection GetAllLightChannels()
{
lock (_sync) { return LightControllers.SelectMany(c => c.ChannelConfigs).OrderBy(ch => ch.GlobalIndex).ToList().AsReadOnly(); }
}
///
/// 根据全局通道索引获取单个通道配置。
///
/// 通道的全局索引(GlobalIndex)。
/// 匹配的 实例,找不到则返回 null。
public ChannelConfig GetLightChannelByGlobalIndex(int globalIndex)
{
lock (_sync) { return LightControllers.SelectMany(c => c.ChannelConfigs).FirstOrDefault(ch => ch.GlobalIndex == globalIndex); }
}
///
/// 判断是否存在指定全局通道索引的通道配置。
///
/// 全局通道索引。
/// 存在返回 true,否则返回 false。
public bool ContainsLightChannelByGlobalIndex(int globalIndex)
{
lock (_sync) { return LightControllers.SelectMany(c => c.ChannelConfigs).Any(ch => ch.GlobalIndex == globalIndex); }
}
///
/// 更新指定全局通道的默认亮度(DefaultBrightness),并持久化光源控制器配置。
///
/// 目标通道的全局索引(GlobalIndex)。
/// 要设置的亮度值。
/// 更新成功返回 true;找不到对应通道返回 false。
public bool UpdateChannelDefaultBrightness(int globalIndex, int brightness)
{
lock (_sync)
{
var ch = LightControllers.SelectMany(c => c.ChannelConfigs).FirstOrDefault(x => x.GlobalIndex == globalIndex);
if (ch == null) return false;
ch.DefaultBrightness = brightness;
FileHelper.WriteJsonFile(LightControllers, ConfigPaths.LightControllersConfigurationPath);
return true;
}
}
#endregion
#region Plc Addresses
public PlcAddressConfig GetPlcAddresses() { lock (_sync) { return PlcAddressConfig; } }
#endregion
///
/// 释放资源并保存当前内存中的所有配置到文件。
///
public void Dispose()
{
SaveAll();
GC.SuppressFinalize(this);
}
}
}