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; 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; } 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); } } } 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); } } public Task SaveAllAsync() { return Task.Run(() => SaveAll()); } #region Cameras public IReadOnlyCollection GetAllCameras() { lock (_sync) { return Cameras.ToList().AsReadOnly(); } } public CameraInfo GetCamera(Guid id) { lock (_sync) { return Cameras.FirstOrDefault(c => c.Id == id); } } 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; } 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; } 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(); } } public RobotInfo GetRobot(Guid id) { lock (_sync) { return Robots.FirstOrDefault(r => r.Id == id); } } 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; } 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; } public bool ContainsRobot(Guid id) { lock (_sync) { return Robots.Any(r => r.Id == id); } } /// /// 修改指定机器人id的步进距离 /// /// 机器人唯一标识符。 /// 步进距离 /// 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的步进距离 /// /// /// 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(); } } public FeederInfo GetFeeder(Guid id) { lock (_sync) { return Feeders.FirstOrDefault(f => f.Id == id); } } 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; } 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; } public bool ContainsFeeder(Guid id) { lock (_sync) { return Feeders.Any(f => f.Id == id); } } #endregion #region PLC public IReadOnlyCollection GetAllPlcs() { lock (_sync) { return Plcs.ToList().AsReadOnly(); } } public PlcInfo GetPlc(Guid id) { lock (_sync) { return Plcs.FirstOrDefault(p => p.Id == id); } } 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; } 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; } public bool ContainsPlc(Guid id) { lock (_sync) { return Plcs.Any(p => p.Id == id); } } #endregion #region BgTcp public BgTcpIP GetBgTcp() { lock (_sync) { return BgCommunicate; } } public void SetBgTcp(BgTcpIP cfg) { lock (_sync) { BgCommunicate = cfg; } } public void SaveBgTcp() { lock (_sync) { FileHelper.WriteJsonFile(BgCommunicate, ConfigPaths.BgTcpIpConfigurationPath); } } #endregion #region BgModbus public BgModbusTcp GetBgModbusTcp() { lock (_sync) { return BgModbusCommunicate; } } public void SetBgModbusTcp(BgModbusTcp cfg) { lock (_sync) { BgModbusCommunicate = cfg; } } 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(); } } /// /// 新增或更新清料任务 /// /// /// 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; } /// /// 移除清料任务 /// /// /// 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); } /// /// 判断是否包含指定标识的清料任务 /// /// /// public bool ContainsClearanceTask(Guid id) { lock (_sync) { return FeederClearanceTasks.Any(t => t.Id == id); } } /// /// 根据标识获取单个清料任务信息 /// /// /// public FeederClearWork GetClearanceTask(Guid id) { lock (_sync) { return FeederClearanceTasks.FirstOrDefault(t => t.Id == id); } } /// /// 判断是否包含指定编号的清料任务 /// /// /// public bool ContainsClearanceTaskByNumber(int taskNumber) { lock (_sync) { return FeederClearanceTasks.Any(t => t.TaskCode == taskNumber); } } /// /// 根据编号获取单个清料任务信息 /// /// /// 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 public void Dispose() { SaveAll(); GC.SuppressFinalize(this); } } }