using Cognex.VisionPro; using Cognex.VisionPro.ToolBlock; using SqlSugar; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using TeamAAS_VP.Core; using TeamAAS_VP.Data; // for ISystemDatabaseService (if used) using TeamAAS_VP.Enums; 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; using TeamAAS_VP.Resources.Languages; using WPFLocalizeExtension.Engine; 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"; public static readonly string DeviceInfoConfigurationPath = "..//Config//DeviceInfo.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"; // 螺丝供料器(物料)配置路径 public static readonly string ScrewFeedersConfigurationPath = "..//Config//ScrewFeedersConfiguration.cfg"; // 刷卡器配置路径 public static readonly string CardReaderConfigurationPath = "..//Config//CardReaderConfiguration.cfg"; // 生产数据配置路径 public static readonly string DataConfigurationPath = "..//Config//DataConfiguration.cfg"; } private readonly object _sync = new object(); private ObservableCollection Cameras { get; set; } private ObservableCollection Robots { get; set; } private ObservableCollection Feeders { get; set; } // 螺丝供料器(物料)集合 private ObservableCollection ScrewFeeders { 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; } // Device info - 支持多设备 private List _deviceInfoList; private PlcAddressConfig PlcAddressConfig { get; set; } // 单个电批配置 private TeamAAS_VP.Models.ScrewDriver.ScrewDriverConfig _screwDriverConfig; public TeamAAS_VP.Models.ScrewDriver.ScrewDriverConfig ScrewDriverConfig { get { lock(_sync) { return _screwDriverConfig; } } } private ISystemDatabaseService _systemDatabaseService; // 刷卡器配置 private SerialPortConfig _cardReaderConfig; /// /// 注入系统数据库服务(可选),用于记录螺丝批次变更等记录。 /// /// public void SetSystemDatabaseService(ISystemDatabaseService svc) { lock (_sync) { _systemDatabaseService = svc; } } /// /// 将螺丝供料器批次变更记录写入数据库(若已注入 ISystemDatabaseService)。 /// public async System.Threading.Tasks.Task RecordScrewFeederBatchAsync(ScrewFeederBatchRecord record) { if (record == null) throw new ArgumentNullException(nameof(record)); try { if (_systemDatabaseService != null) { await _systemDatabaseService.RecordScrewFeederBatchAsync(record); } } catch { // 忽略数据库写入错误,记录可添加日志 } } /// /// 初始化一个新的 实例。 /// 构造函数不会自动加载配置文件(避免在构造期间进行 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); } // load screw feeders (material) configuration if (File.Exists(ConfigPaths.ScrewFeedersConfigurationPath)) ScrewFeeders = FileHelper.ReadJsonFile>(ConfigPaths.ScrewFeedersConfigurationPath); else { ScrewFeeders = new ObservableCollection(); FileHelper.WriteJsonFile(ScrewFeeders, ConfigPaths.ScrewFeedersConfigurationPath); } 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); } // load device info list (支持多设备) if (File.Exists(ConfigPaths.DeviceInfoConfigurationPath)) { try { // 尝试作为列表读取 _deviceInfoList = FileHelper.ReadJsonFile>(ConfigPaths.DeviceInfoConfigurationPath); } catch { // 兼容旧版单个 DeviceInfo,将其转换为列表 var singleDevice = FileHelper.ReadJsonFile(ConfigPaths.DeviceInfoConfigurationPath); if (singleDevice != null) { singleDevice.DeviceNo = 0; _deviceInfoList = new List { singleDevice }; } else { _deviceInfoList = new List { new DeviceInfo { DeviceNo = 0 } }; } FileHelper.WriteJsonFile(_deviceInfoList, ConfigPaths.DeviceInfoConfigurationPath); } } else { _deviceInfoList = new List { new DeviceInfo { DeviceNo = 0 } }; FileHelper.WriteJsonFile(_deviceInfoList, ConfigPaths.DeviceInfoConfigurationPath); } // load card reader configuration if (File.Exists(ConfigPaths.CardReaderConfigurationPath)) _cardReaderConfig = FileHelper.ReadJsonFile(ConfigPaths.CardReaderConfigurationPath); else { _cardReaderConfig = new SerialPortConfig(); FileHelper.WriteJsonFile(_cardReaderConfig, ConfigPaths.CardReaderConfigurationPath); } } } /// /// 异步执行 操作。 /// /// 表示异步加载操作的任务。 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(FeederClearanceTasks, ConfigPaths.FeederClearanceTasksConfigurationPath); FileHelper.WriteJsonFile(LightControllers, ConfigPaths.LightControllersConfigurationPath); // save screw driver config if (_screwDriverConfig != null) FileHelper.WriteJsonFile(_screwDriverConfig, ConfigPaths.ScrewDriverConfigurationPath); // save screw feeders if (ScrewFeeders != null) FileHelper.WriteJsonFile(ScrewFeeders, ConfigPaths.ScrewFeedersConfigurationPath); // save plc addresses //FileHelper.WriteJsonFile(PlcAddressConfig, "..//Config//PlcAddressConfiguration.cfg"); // save device info list (支持多设备) if (_deviceInfoList != null) FileHelper.WriteJsonFile(_deviceInfoList, ConfigPaths.DeviceInfoConfigurationPath); // save card reader config if (_cardReaderConfig != null) FileHelper.WriteJsonFile(_cardReaderConfig, ConfigPaths.CardReaderConfigurationPath); } } /// /// 异步执行 操作。 /// /// 表示异步保存操作的任务。 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 DeviceInfo /// /// 获取所有设备信息配置列表 /// /// 设备信息列表的只读集合 public IReadOnlyCollection GetAllDeviceInfos() { lock (_sync) { if (_deviceInfoList == null) { if (File.Exists(ConfigPaths.DeviceInfoConfigurationPath)) { try { _deviceInfoList = FileHelper.ReadJsonFile>(ConfigPaths.DeviceInfoConfigurationPath); } catch { // 兼容旧版单个 DeviceInfo var singleDevice = FileHelper.ReadJsonFile(ConfigPaths.DeviceInfoConfigurationPath); if (singleDevice != null) { singleDevice.DeviceNo = 0; _deviceInfoList = new List { singleDevice }; } else { _deviceInfoList = new List { new DeviceInfo { DeviceNo = 0 } }; } FileHelper.WriteJsonFile(_deviceInfoList, ConfigPaths.DeviceInfoConfigurationPath); } } else { _deviceInfoList = new List { new DeviceInfo { DeviceNo = 0 } }; FileHelper.WriteJsonFile(_deviceInfoList, ConfigPaths.DeviceInfoConfigurationPath); } } return _deviceInfoList.AsReadOnly(); } } /// /// 根据设备编号获取设备信息(兼容旧API,返回第一个设备) /// /// 设备信息,若不存在则创建默认设备 public DeviceInfo GetDeviceInfo() { return GetDeviceInfo(0); } /// /// 根据设备编号获取设备信息 /// /// 设备编号 /// 设备信息,若不存在则返回 null public DeviceInfo GetDeviceInfo(int deviceNo) { lock (_sync) { var allDevices = GetAllDeviceInfos(); return allDevices.FirstOrDefault(d => d.DeviceNo == deviceNo); } } /// /// 添加或更新设备信息 /// /// 设备信息对象 public void SaveDeviceInfo(DeviceInfo deviceInfo) { if (deviceInfo == null) return; lock (_sync) { // 确保列表已初始化 if (_deviceInfoList == null) { _deviceInfoList = new List(); } // 查找是否已存在相同设备编号的设备 var existing = _deviceInfoList.FirstOrDefault(d => d.DeviceNo == deviceInfo.DeviceNo); if (existing != null) { // 更新现有设备 var index = _deviceInfoList.IndexOf(existing); _deviceInfoList[index] = deviceInfo; } else { // 添加新设备 _deviceInfoList.Add(deviceInfo); } // 按设备编号排序 _deviceInfoList = _deviceInfoList.OrderBy(d => d.DeviceNo).ToList(); FileHelper.WriteJsonFile(_deviceInfoList, ConfigPaths.DeviceInfoConfigurationPath); } } /// /// 保存所有设备信息列表 /// /// 设备信息列表 public void SaveAllDeviceInfos(IEnumerable deviceInfoList) { if (deviceInfoList == null) return; lock (_sync) { _deviceInfoList = deviceInfoList.OrderBy(d => d.DeviceNo).ToList(); FileHelper.WriteJsonFile(_deviceInfoList, ConfigPaths.DeviceInfoConfigurationPath); } } /// /// 添加新设备 /// /// 新添加的设备 public DeviceInfo AddDeviceInfo() { lock (_sync) { if (_deviceInfoList == null) { _deviceInfoList = new List(); } // 计算新设备编号 int newDeviceNo = _deviceInfoList.Count > 0 ? _deviceInfoList.Max(d => d.DeviceNo) + 1 : 0; var newDevice = new DeviceInfo { DeviceNo = newDeviceNo }; _deviceInfoList.Add(newDevice); FileHelper.WriteJsonFile(_deviceInfoList, ConfigPaths.DeviceInfoConfigurationPath); return newDevice; } } /// /// 删除设备 /// /// 设备编号 /// 是否删除成功 public bool RemoveDeviceInfo(int deviceNo) { lock (_sync) { if (_deviceInfoList == null) return false; var device = _deviceInfoList.FirstOrDefault(d => d.DeviceNo == deviceNo); if (device != null) { _deviceInfoList.Remove(device); FileHelper.WriteJsonFile(_deviceInfoList, ConfigPaths.DeviceInfoConfigurationPath); return true; } return false; } } #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); } } /// /// 获取当前语言 /// /// public Language GetCurrentLanguage() { lock (_sync) { var config = GetSystemConfiguration(); return config.CurrentLanguage; } } /// /// 设置当前语言 /// /// public void SetCurrentLanguage(Language language) { lock (_sync) { var config = GetSystemConfiguration(); config.CurrentLanguage = language; if (language == Enums.Language.ChineseSimplified) { var culture = new CultureInfo("zh-CN"); App.Current.Dispatcher.Thread.CurrentCulture = culture; App.Current.Dispatcher.Thread.CurrentUICulture = culture; LocalizeDictionary.Instance.Culture = culture; Lang.Culture = culture; System.Windows.Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Microsoft YaHei"); } else if (language == Enums.Language.ChineseTraditional) { var culture = new CultureInfo("zh-TW"); App.Current.Dispatcher.Thread.CurrentCulture = culture; App.Current.Dispatcher.Thread.CurrentUICulture = culture; LocalizeDictionary.Instance.Culture = culture; Lang.Culture = culture; System.Windows.Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Microsoft YaHei"); } else if (language == Enums.Language.English) { var culture = new CultureInfo("en"); App.Current.Dispatcher.Thread.CurrentCulture = culture; App.Current.Dispatcher.Thread.CurrentUICulture = culture; LocalizeDictionary.Instance.Culture = culture; Lang.Culture = culture; System.Windows.Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Segoe UI"); } else if (language == Enums.Language.Japanese) { var culture = new CultureInfo("ja-JP"); App.Current.Dispatcher.Thread.CurrentCulture = culture; App.Current.Dispatcher.Thread.CurrentUICulture = culture; LocalizeDictionary.Instance.Culture = culture; Lang.Culture = culture; System.Windows.Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Segoe UI"); } else if (language == Enums.Language.Vietnamese) { var culture = new CultureInfo("vi"); App.Current.Dispatcher.Thread.CurrentCulture = culture; App.Current.Dispatcher.Thread.CurrentUICulture = culture; LocalizeDictionary.Instance.Culture = culture; Lang.Culture = culture; System.Windows.Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Segoe UI"); } SaveSystemConfiguration(config); } } #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 #region Screw Feeders (material) /// /// 获取所有螺丝供料器配置的只读集合。 /// public IReadOnlyCollection GetAllScrewFeeders() { lock (_sync) { return ScrewFeeders.ToList().AsReadOnly(); } } /// /// 根据 Id 获取单个螺丝供料器配置。 /// public ScrewFeederInfo GetScrewFeeder(Guid id) { lock (_sync) { return ScrewFeeders.FirstOrDefault(s => s.Id == id); } } /// /// 新增或更新螺丝供料器配置。 /// 新增时若未指定 FeederNumber 则分配为当前数量+1。 /// public ScrewFeederInfo AddOrUpdateScrewFeeder(ScrewFeederInfo feeder) { if (feeder == null) return null; lock (_sync) { var exist = ScrewFeeders.FirstOrDefault(f => f.Id == feeder.Id); if (exist != null) { var idx = ScrewFeeders.IndexOf(exist); ScrewFeeders[idx] = feeder; } else { if (feeder.FeederNumber <= 0) feeder.FeederNumber = ScrewFeeders.Count + 1; ScrewFeeders.Add(feeder); } FileHelper.WriteJsonFile(ScrewFeeders, ConfigPaths.ScrewFeedersConfigurationPath); } return feeder; } /// /// 移除螺丝供料器配置。 /// public bool RemoveScrewFeeder(Guid id) { bool result = false; lock (_sync) { var e = ScrewFeeders.FirstOrDefault(s => s.Id == id); if (e != null) result = ScrewFeeders.Remove(e); } FileHelper.WriteJsonFile(ScrewFeeders, ConfigPaths.ScrewFeedersConfigurationPath); return result; } public bool ContainsScrewFeeder(Guid id) { lock (_sync) { return ScrewFeeders.Any(s => s.Id == id); } } // event for low level alarm public event Action ScrewFeederLowLevelAlarmTriggered; /// /// 获取指定供料器编号的剩余螺丝数量(找不到返回 -1) /// public int GetRemainingCountByFeederNumber(int feederNumber) { lock (_sync) { var f = ScrewFeeders.FirstOrDefault(s => s.FeederNumber == feederNumber); if (f == null) return -1; return f.RemainingCount; } } /// /// 获取指定供料器编号的低量报警状态(找不到返回 false) /// public bool GetLowLevelAlarmStatusByFeederNumber(int feederNumber) { lock (_sync) { var f = ScrewFeeders.FirstOrDefault(s => s.FeederNumber == feederNumber); if (f == null) return false; return f.IsLowLevelAlarmActive; } } // helper: check and raise low level alarm when necessary private void CheckAndTriggerLowLevelAlarm(ScrewFeederInfo f) { if (f == null) return; if (f.LowLevelAlarmEnabled && f.RemainingCount <= f.LowLevelThreshold) { if (!f.IsLowLevelAlarmActive) { f.IsLowLevelAlarmActive = true; try { ScrewFeederLowLevelAlarmTriggered?.Invoke(f.Clone()); } catch { } } } else { if (f.IsLowLevelAlarmActive) { f.IsLowLevelAlarmActive = false; } } } /// /// 为指定供料器编号递减指定数量,若数量不足返回 false /// public bool DecrementScrewCountByFeederNumber(int feederNumber, int decrement = 1) { lock (_sync) { var f = ScrewFeeders.FirstOrDefault(s => s.FeederNumber == feederNumber); if (f == null) return false; if (decrement <= 0) decrement = 1; if (f.RemainingCount < decrement) return false; f.RemainingCount -= decrement; // check alarm CheckAndTriggerLowLevelAlarm(f); FileHelper.WriteJsonFile(ScrewFeeders, ConfigPaths.ScrewFeedersConfigurationPath); return true; } } /// /// 清除指定供料器编号的螺丝信息(批次号置空,剩余数量置0) /// public void ClearScrewFeederByNumber(int feederNumber) { lock (_sync) { var f = ScrewFeeders.FirstOrDefault(s => s.FeederNumber == feederNumber); if (f == null) return; f.BatchNumber = string.Empty; f.RemainingCount = 0; // check alarm CheckAndTriggerLowLevelAlarm(f); FileHelper.WriteJsonFile(ScrewFeeders, ConfigPaths.ScrewFeedersConfigurationPath); } } /// /// 为指定供料器编号设置批次号,并在原有剩余数量上增加一包(PackSize) /// public void SetBatchForFeederNumber(int feederNumber, string batchNumber) { if (string.IsNullOrWhiteSpace(batchNumber)) return; // capture values under lock, then record to DB outside lock ScrewFeederInfo target = null; int remainingAfter = 0; int packSize = 0; string feederName = null; lock (_sync) { var f = ScrewFeeders.FirstOrDefault(s => s.FeederNumber == feederNumber); if (f == null) return; f.BatchNumber = batchNumber; f.RemainingCount += Math.Max(0, f.PackSize); // prepare capture values target = f; remainingAfter = f.RemainingCount; packSize = f.PackSize; feederName = f.Name; // check alarm CheckAndTriggerLowLevelAlarm(f); FileHelper.WriteJsonFile(ScrewFeeders, ConfigPaths.ScrewFeedersConfigurationPath); } try { var record = new ScrewFeederBatchRecord { BatchNumber = batchNumber, PackSize = packSize, ChangeTime = DateTime.Now, RemainingCount = remainingAfter, FeederNumber = feederNumber, FeederName = feederName }; // try get operator name from system database service if available try { var user = _systemDatabaseService?.GetCurrentUser(); if (user != null) record.OperatorName = user.UserName; } catch { // ignore getting user failure } // fire-and-forget recording (ConfigService will forward to DB service if set) _ = RecordScrewFeederBatchAsync(record); } catch { // ignore any error creating/recording the batch record } } #endregion #region 用户登录刷卡器 /// /// 获取刷卡器配置 /// /// public SerialPortConfig GetCardReaderConfig() { lock (_sync) { if (_cardReaderConfig != null) return _cardReaderConfig; if (File.Exists(ConfigPaths.CardReaderConfigurationPath)) { try { _cardReaderConfig = FileHelper.ReadJsonFile(ConfigPaths.CardReaderConfigurationPath); } catch { // 若读取失败,创建默认配置并覆盖文件(与项目中其他配置策略一致) _cardReaderConfig = new SerialPortConfig(); try { FileHelper.WriteJsonFile(_cardReaderConfig, ConfigPaths.CardReaderConfigurationPath); } catch { } } } else { _cardReaderConfig = new SerialPortConfig(); try { FileHelper.WriteJsonFile(_cardReaderConfig, ConfigPaths.CardReaderConfigurationPath); } catch { } } return _cardReaderConfig; } } /// /// 保存刷卡器配置 /// /// public void SaveCardReaderConfig(SerialPortConfig config) { if (config == null) return; lock (_sync) { _cardReaderConfig = config; try { FileHelper.WriteJsonFile(_cardReaderConfig, ConfigPaths.CardReaderConfigurationPath); } catch { // 忽略持久化错误(与项目中其它保存方法一致),可在需要时添加日志记录 } } } #endregion /// /// 释放资源并保存当前内存中的所有配置到文件。 /// public void Dispose() { SaveAll(); GC.SuppressFinalize(this); } } }