using MembranePoreTester.Communication; using System.Collections.ObjectModel; using System.ComponentModel; // 用于 PropertyChanged using System.Net; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Threading; using static OfficeOpenXml.ExcelErrorValue; namespace MembranePoreTester.ViewModels { public class MainViewModel : ViewModelBase { public ObservableCollection Stations { get; } = new(); // 报警信息集合(用于显示多条) private ObservableCollection _alarmMessages; public ObservableCollection AlarmMessages { get => _alarmMessages; set => SetProperty(ref _alarmMessages, value); } private bool _hasAlarm; public bool HasAlarm { get => _hasAlarm; set => SetProperty(ref _hasAlarm, value); } private string _alarmText; public string AlarmText { get => _alarmText; set => SetProperty(ref _alarmText, value); } private DispatcherTimer _alarmTimer; public class PressureModeItem { public string Text { get; set; } public int Value { get; set; } public override string ToString() { return Text; // 直接返回要显示的文本 } } public class StationItem : ViewModelBase { private PressureModeItem _selectedPressureMode; private List _pressureModeList; public List PressureModeList => _pressureModeList ??= new List { new PressureModeItem { Text = "低压", Value = 1 }, new PressureModeItem { Text = "高压", Value = 0 } }; public PressureModeItem SelectedPressureMode { get => _selectedPressureMode; set { if (SetProperty(ref _selectedPressureMode, value)) { Task.Run(async () => await WritePressureModeAsync(value?.Text ?? "低压")); // 切换模式后调整加压上限 AdjustPressureLimitForMode(); } } } private PressureModeItem _selectedInTakeMode; private List _inTakeModeList; public List InTakeModeList => _inTakeModeList ??= new List { new PressureModeItem { Text = "底部进气", Value = 0 }, //new PressureModeItem { Text = "顶部进气", Value = 1 } }; public PressureModeItem SelecteInTakeMode { get => _selectedInTakeMode; set { if (SetProperty(ref _selectedInTakeMode, value)) { Task.Run(async () => await WriteInTakeModeAsync(value?.Text ?? "底部进气")); } } } private readonly IPlcService _plcService; private readonly PlcConfiguration _plcConfig; private bool _isPressing; private string _pressButtonText = "加压"; private string _highLowPressure = "低压"; private bool _enableStatus; // M21 状态 public string Name { get; set; } public BubblePointViewModel BubblePointVM { get; set; } public PoreDistributionViewModel PoreDistributionVM { get; set; } public int StationId { get; set; } public string HighLowPressure { get => _selectedPressureMode?.Text ?? "低压"; set { var mode = PressureModeList.FirstOrDefault(m => m.Text == value); if (mode != null) { SelectedPressureMode = mode; } } } public string UpAndDown { get => _selectedInTakeMode?.Text ?? "底部"; set { var mode = InTakeModeList.FirstOrDefault(m => m.Text == value); if (mode != null) { SelecteInTakeMode = mode; } } } public string PressButtonText { get => _pressButtonText; set => SetProperty(ref _pressButtonText, value); } public bool EnableStatus { get => _enableStatus; private set { if (SetProperty(ref _enableStatus, value)) { // 当设备停止运行时,停止孔分布自动采集 if (!value) { PoreDistributionVM?.StopCollecting(); } OnPropertyChanged(nameof(EnableStatusText)); OnPropertyChanged(nameof(EnableStatusColor)); } } } // 使能状态显示文本 public string EnableStatusText => EnableStatus ? "运行中" : "未启动"; // 使能状态显示颜色(绿色表示运行中,灰色表示未启动) // 使能状态显示颜色(更亮的颜色) public string EnableStatusColor => EnableStatus ? "#00FF00" : "#FF3333"; // 定时器,用于轮询 M21 状态 private System.Windows.Threading.DispatcherTimer _timer; public ICommand PressCommand { get; } //public ICommand BurstCommand { get; } public ICommand StartCommand { get; } public ICommand StopCommand { get; } public ICommand EnableCommand { get; } // 备用,但可以使用复选框直接绑定 private double _pressureUpperLimit; private double _pressureRate; // 防止从PLC读取时触发校验弹窗 private bool _suppressPressureValidation = false; public double PressureUpperLimit { get => _pressureUpperLimit; set { // 如果不是从PLC读取(即用户交互或程序主动设置),先校验再设置 if (!_suppressPressureValidation && HighLowPressure.Contains("低压") && value > 200) { MessageBox.Show("低压模式,加压上限不能超过200!"); return; } if (!_suppressPressureValidation && HighLowPressure.Contains("高压") && value > 1000) { MessageBox.Show("高压模式,加压上限不能超过1000!"); return; } bool changed = SetProperty(ref _pressureUpperLimit, value); // 仅在非抑制模式下将改变写回PLC,避免把PLC读回的值再次写入造成循环 if (changed && !_suppressPressureValidation) { ushort address = StationId == 1 ? _plcConfig.PressureUpperLimit : StationId == 2 ? _plcConfig.PressureUpperLimit2 : _plcConfig.PressureUpperLimit3; _ = _plcService.WriteMultipleRegistersAsync(address, (float)value); } } } public double PressureRate { get => _pressureRate; set { if (SetProperty(ref _pressureRate, value)) { // 值改变时写入PLC _ = _plcService.WriteMultipleRegistersAsync(StationId == 1 ? _plcConfig.PressureRate : StationId == 2 ? _plcConfig.PressureRate2 : _plcConfig.PressureRate3, (float)value); } } } // 添加读取这两个参数的方法 private async Task ReadPressureParametersAsync() { try { // 根据工位选择加压上限的PLC地址 ushort upperLimitAddress = StationId == 1 ? _plcConfig.PressureUpperLimit : StationId == 2 ? _plcConfig.PressureUpperLimit2 : _plcConfig.PressureUpperLimit3; float upperLimit = await _plcService.ReadFloatAsync(upperLimitAddress); float rate = await _plcService.ReadFloatAsync(StationId == 1 ? _plcConfig.PressureRate : StationId == 2 ? _plcConfig.PressureRate2 : _plcConfig.PressureRate3); Application.Current.Dispatcher.Invoke(() => { // 在将PLC读取的值赋回到属性时,抑制用户校验提示和避免回写PLC _suppressPressureValidation = true; PressureUpperLimit = upperLimit; PressureRate = rate; _suppressPressureValidation = false; }); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"读取压力参数失败: {ex.Message}"); } } public StationItem() { if (IsInDesignMode()) { return; // 设计时跳过PLC初始化 } _plcService = App.PlcService; _plcConfig = App.PlcConfig; PressCommand = new RelayCommand(async () => await TogglePressAsync()); // 在 StationItem 构造函数中 StartCommand = new RelayCommand(async () => { // 启动PLC await WriteCoilAsync(StationId == 1 ? _plcConfig.StartCoil : StationId == 2 ? _plcConfig.StartCoil2 : _plcConfig.StartCoil3, true); // 启动孔分布自动采集 PoreDistributionVM.StartCollecting(); }); StopCommand = new RelayCommand(async () => { // 停止自动采集 PoreDistributionVM.StopCollecting(); // 停止PLC await WriteCoilAsync(StationId == 1 ? _plcConfig.StopCoil : StationId == 2 ? _plcConfig.StopCoil2 : _plcConfig.StopCoil3, true); }); // 启动定时器,每秒读取一次 M21 状态 _timer = new System.Windows.Threading.DispatcherTimer(); _timer.Interval = TimeSpan.FromSeconds(1); _timer.Tick += async (s, e) => { await ReadEnableStatusAsync(); await ReadPressureParametersAsync(); // 新增:读取压力参数 }; _timer.Start(); // 延迟2秒后读取,确保连接稳定 Task.Delay(2000).ContinueWith(async _ => { await ReadPressureModeAsync(); await ReadPressureParametersAsync(); // 新增:读取压力参数 await ReadInTakeModeAsync(); }, TaskScheduler.Default); } private async Task ReadPressureModeAsync() { await SafeExecuteAsync($"ReadPressureModeAsync{StationId}", async () => { try { ushort[] values = await _plcService.ReadHoldingRegistersAsync(StationId == 1 ? _plcConfig.PressureModeRegister : StationId == 2 ? _plcConfig.PressureModeRegister2 : _plcConfig.PressureModeRegister3, 1); ushort val = values[0]; string newValue = val == 0 ? "高压" : "低压"; Application.Current.Dispatcher.Invoke(() => { // 更新选中项 HighLowPressure = newValue; }); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"读取压力模式失败: {ex.Message}"); } }); } private async Task ReadInTakeModeAsync() { await SafeExecuteAsync($"ReadInTakeModeAsync{StationId}", async () => { try { ushort address = 0; if (StationId == 1) { address = _plcConfig.UpAndDown1; } else if (StationId == 2) { address = _plcConfig.UpAndDown2; } else if (StationId == 3) { address = _plcConfig.UpAndDown3; } ushort[] values = await _plcService.ReadHoldingRegistersAsync(address, 1); ushort val = values[0]; string newValue = val == 0 ? "底部进气" : "顶部进气"; Application.Current.Dispatcher.Invoke(() => { // 更新选中项 UpAndDown = newValue; }); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"读取出口模式失败: {ex.Message}"); } }); } private async Task TogglePressAsync() { try { ushort coilAddress = _plcConfig.PressCoil; // 根据工位选择不同的线圈地址 switch (StationId) { case 1: coilAddress = _plcConfig.PressCoil; break; case 2: coilAddress = _plcConfig.PressCoil2; break; case 3: coilAddress = _plcConfig.PressCoil3; break; } // 先读取PLC当前的加压状态 bool currentStatus = await _plcService.ReadCoilAsync(coilAddress); // 切换状态 _isPressing = !currentStatus; // 写入新状态到PLC await WriteCoilAsync(coilAddress, _isPressing); // 更新按钮文字 PressButtonText = _isPressing ? "停止加压" : "加压"; } catch (Exception ex) { MessageBox.Show($"读取加压状态失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } // 在 StationItem 类中添加字段 private bool _lastEnableReadFailed = false; private DateTime _lastEnableErrorTime = DateTime.MinValue; private async Task ReadEnableStatusAsync() { await SafeExecuteAsync($"ReadEnableStatus_Station{StationId}", async () => { try { bool status = await _plcService.ReadCoilAsync(StationId == 1 ? _plcConfig.EnableCoil : StationId == 2 ? _plcConfig.EnableCoil2 : _plcConfig.EnableCoil3); // 读取 M21 EnableStatus = status; bool pressStatus = await _plcService.ReadCoilAsync(StationId == 1 ? _plcConfig.PressCoil : StationId == 2 ? _plcConfig.PressCoil2 : _plcConfig.PressCoil3);//这里也要更新加压的按钮状态 _isPressing = pressStatus; // 在UI线程更新按钮文字 Application.Current.Dispatcher.Invoke(() => { PressButtonText = pressStatus ? "停止加压" : "加压"; }); } catch (Exception ex) { // 读取出错时保持原状态或显示错误 System.Diagnostics.Debug.WriteLine($"读取使能状态失败: {ex.Message}"); } }); } private async Task WriteCoilAsync(ushort coil, bool value) { try { await _plcService.WriteCoilAsync(coil, value); } catch (Exception ex) { MessageBox.Show($"写线圈失败: {ex.Message}"); } } private async Task WritePressureModeAsync(string mode) { if (IsDisposed) return; ushort val = mode.ToString().Contains("高压") ? (ushort)0 : (ushort)1; try { if (StationId == 1) { await _plcService.WriteSingleRegisterAsync(_plcConfig.PressureModeRegister, val); } else if (StationId == 2) { await _plcService.WriteSingleRegisterAsync(_plcConfig.PressureModeRegister2, val); } else if (StationId == 3) { await _plcService.WriteSingleRegisterAsync(_plcConfig.PressureModeRegister3, val); } } catch (Exception ex) { MessageBox.Show($"写压力模式失败: {ex.Message}"); } } private async Task WriteInTakeModeAsync(string mode) { if (IsDisposed) return; ushort val = mode.ToString().Contains("底部") ? (ushort)0 : (ushort)1; try { ushort address = 0; if (StationId == 1) { address = _plcConfig.UpAndDown1; } else if (StationId == 2) { address = _plcConfig.UpAndDown2; } else if (StationId == 3) { address = _plcConfig.UpAndDown3; } await _plcService.WriteSingleRegisterAsync(address, val); } catch (Exception ex) { MessageBox.Show($"写出口模式失败: {ex.Message}"); } } private bool _isPoreDistributionActive; public bool IsPoreDistributionActive { get => _isPoreDistributionActive; set => SetProperty(ref _isPoreDistributionActive, value); } // 在 StationItem 类中添加以下方法 private void AdjustPressureLimitForMode() { bool isLowPressure = (SelectedPressureMode?.Text == "低压"); double currentLimit = _pressureUpperLimit; double newLimit = currentLimit; if (isLowPressure && currentLimit > 200) newLimit = 200; else if (!isLowPressure && currentLimit > 1000) newLimit = 1000; else return; // 直接修改字段,避免触发 setter 中的校验和重复写 PLC _pressureUpperLimit = newLimit; OnPropertyChanged(nameof(PressureUpperLimit)); // 写回 PLC ushort address = StationId == 1 ? _plcConfig.PressureUpperLimit : StationId == 2 ? _plcConfig.PressureUpperLimit2 : _plcConfig.PressureUpperLimit3; _ = _plcService.WriteMultipleRegistersAsync(address, (float)newLimit); } } public MainViewModel() { for (int i = 1; i <= 3; i++) { var station = new StationItem { Name = $"工位 {i}", BubblePointVM = new BubblePointViewModel(), PoreDistributionVM = new PoreDistributionViewModel(), StationId = i }; // 在构造完成后显式传入 StationId 以初始化 VM station.BubblePointVM.Initialize(i); station.PoreDistributionVM.Initialize(i); Stations.Add(station); } AlarmMessages = new ObservableCollection(); _alarmTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) }; _alarmTimer.Tick += async (s, e) => await RefreshAlarms(); _alarmTimer.Start(); } private async Task RefreshAlarms() { try { var plc = App.PlcService; var config = App.PlcConfig; // 读取4个报警线圈状态 bool smallFlowAlarm = await plc.ReadCoilAsync(config.SmallFlowAlarm); bool bigFlowAlarm = await plc.ReadCoilAsync(config.BigFlowAlarm); bool highPressAlarm = await plc.ReadCoilAsync(config.HighPressAlarm); bool lowPressAlarm = await plc.ReadCoilAsync(config.LowPressAlarm); bool Midnight1 = await plc.ReadCoilAsync(config.Midnight1); bool Midnight2 = await plc.ReadCoilAsync(config.Midnight2); bool Midnight3 = await plc.ReadCoilAsync(config.Midnight3); // 收集当前报警信息 var newAlarms = new List(); if (smallFlowAlarm) newAlarms.Add("小流量计报警"); if (bigFlowAlarm) newAlarms.Add("大流量报警"); if (highPressAlarm) newAlarms.Add("高压超限"); if (lowPressAlarm) newAlarms.Add("低压超限"); if (Midnight1) newAlarms.Add("1工位漏液"); if (Midnight2) newAlarms.Add("2工位漏液"); if (Midnight3) newAlarms.Add("3工位漏液"); // 更新UI(避免频繁刷新集合导致界面闪烁,直接替换内容) Application.Current.Dispatcher.Invoke(() => { AlarmMessages.Clear(); foreach (var msg in newAlarms) AlarmMessages.Add(msg); HasAlarm = newAlarms.Any(); AlarmText = HasAlarm ? string.Join(";", newAlarms) : ""; }); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"读取报警状态失败: {ex.Message}"); } } } }