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; using TeamAAS_VP.Models.ScrewDriver; 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"; // 电批配置路径(单设备) public static readonly string ScrewDriverConfigurationPath = "..//Config//ScrewDriverConfiguration.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; } // 单个电批配置 private TeamAAS_VP.Models.ScrewDriver.ScrewDriverConfig _screwDriverConfig; public TeamAAS_VP.Models.ScrewDriver.ScrewDriverConfig ScrewDriverConfig { get { lock(_sync) { return _screwDriverConfig; } } } /// /// 初始化一个新的 实例。 /// 构造函数不会自动加载配置文件(避免在构造期间进行 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); } // load screw driver config (single device) if (File.Exists(ConfigPaths.ScrewDriverConfigurationPath)) _screwDriverConfig = FileHelper.ReadJsonFile(ConfigPaths.ScrewDriverConfigurationPath); else { _screwDriverConfig = new TeamAAS_VP.Models.ScrewDriver.ScrewDriverConfig(); FileHelper.WriteJsonFile(_screwDriverConfig, ConfigPaths.ScrewDriverConfigurationPath); } } } /// /// 异步执行 操作。 /// /// 表示异步加载操作的任务。 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 screw driver config if (_screwDriverConfig != null) FileHelper.WriteJsonFile(_screwDriverConfig, ConfigPaths.ScrewDriverConfigurationPath); // 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 #region ScrewDriver config (single device) /// /// 获取电批配置(单设备)。 /// /// 当前的 ,若未加载则返回 null。 public ScrewDriverConfig GetScrewDriverConfig() { lock (_sync) { return _screwDriverConfig; } } /// /// 设置并持久化电批配置(单设备)。 /// /// 新的配置对象,不能为空。 public void SetScrewDriverConfig(ScrewDriverConfig cfg) { if (cfg == null) throw new ArgumentNullException(nameof(cfg)); lock (_sync) { _screwDriverConfig = cfg; FileHelper.WriteJsonFile(_screwDriverConfig, ConfigPaths.ScrewDriverConfigurationPath); } } /// /// 仅将当前内存中的电批配置保存到文件。 /// public void SaveScrewDriverConfig() { lock (_sync) { if (_screwDriverConfig != null) FileHelper.WriteJsonFile(_screwDriverConfig, ConfigPaths.ScrewDriverConfigurationPath); } } #endregion /// /// 释放资源并保存当前内存中的所有配置到文件。 /// public void Dispose() { SaveAll(); GC.SuppressFinalize(this); } } }