Files
VacuumPressureMembranePoreS…/ViewModels/MainViewModel.cs
2026-04-14 23:16:56 +08:00

628 lines
22 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<StationItem> Stations { get; } = new();
// 报警信息集合(用于显示多条)
private ObservableCollection<string> _alarmMessages;
public ObservableCollection<string> 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<PressureModeItem> _pressureModeList;
public List<PressureModeItem> PressureModeList => _pressureModeList ??= new List<PressureModeItem>
{
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 ?? "低压"));
}
}
}
private PressureModeItem _selectedInTakeMode;
private List<PressureModeItem> _inTakeModeList;
public List<PressureModeItem> InTakeModeList => _inTakeModeList ??= new List<PressureModeItem>
{
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 > 800)
{
MessageBox.Show("高压模式,加压上限不能超过800!");
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);
}
}
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<string>();
_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<string>();
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}");
}
}
}
}