Files
zijiuqizonghejianyanyi/MainWindow.xaml.cs
GukSang.Jin 88a65233a7 更新
2026-03-16 19:10:33 +08:00

678 lines
23 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 Modbus.Device;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using .Data;
namespace
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
private MainWindow _mainWindow;
private MainWindow2 _mainWindow2;
private MainWindow3 _mainWindow3;
private MainWindow4 _mainWindow4;
private ParameterSettingsWindow _mainWindow5;
private ReportWindow _reportWindow;
private bool _wasRunning = false; // 上次检验的运行状态
private bool _recordAdded = false; // 本轮测试是否已插入记录
private DateTime _testStartTime; // 测试开始时间
private Button _selectedParameterButton;
private static readonly Brush ParameterButtonDefaultBrush = CreateParameterButtonBrush("#3498DB");
private static readonly Brush ParameterButtonSelectedBrush = CreateParameterButtonBrush("#F39C12");
int retryCount = 0; // 连接失败重试次数
private TcpClient _tcpClient => ModbusResourceManager.Instance.TcpClient;
private IModbusMaster _modbusMaster => ModbusResourceManager.Instance.ModbusMaster;
private const byte SlaveId = 1; // PLC从站地址
private const ushort Address262 = 300;
private bool _isManualInput = false; // 手动输入标记true=输入中暂停读取false=正常读取
private readonly DispatcherTimer _readTimer;
Function ma;
DataChange c = new DataChange();
public MainWindow()
{
InitializeComponent();
_readTimer = InitDispatcherTimer();
//InitializeModbusTcp();
BindInputFocusEvents();
}
private void BindInputFocusEvents()
{
// 地址400输入框count
settingtime.GotFocus += (s, e) => _isManualInput = true; // 获得焦点→暂停读取
settingtime.LostFocus += (s, e) => _isManualInput = false; // 失去焦点→恢复读取
}
private DispatcherTimer InitDispatcherTimer()
{
var timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(500)
};
timer.Tick += async (s, e) =>
{
if (!_isManualInput && _modbusMaster != null)
{
try
{
await ReadAllPlcDataAsync();
}
catch { }
}
};
return timer;
}
private async System.Threading.Tasks.Task ReadAllPlcDataAsync()
{
try
{
// 检查连接状态
if (_tcpClient == null || !_tcpClient.Connected || _modbusMaster == null)
{
return;
}
// 仅在非手动输入时读取设置地址(避免覆盖输入)
if (!_isManualInput)
{
await ReadAddr262DataAsync();
await System.Threading.Tasks.Task.Delay(50);
await ReadAddr404DataAsync();
await System.Threading.Tasks.Task.Delay(50);
await ReadAddr406DataAsync();
await System.Threading.Tasks.Task.Delay(50);
await ReadAddr130DataAsync();
}
await ReadAddrStatusDataAsync();
}
catch (Exception ex)
{
retryCount++;
if (retryCount > 3)
{
ShowErrorMsg($"读取PLC数据失败{ex.Message}");
retryCount = 0;
return;
}
await System.Threading.Tasks.Task.Delay(500);
await ReadAllPlcDataAsync();
}
}
private async System.Threading.Tasks.Task ReadAddr262DataAsync()
{
try
{
ushort[] registers = await System.Threading.Tasks.Task.Run(() =>
_modbusMaster.ReadHoldingRegisters(1, Address262, 2)
);
if (registers != null)
{
int addr406Value = registers[0];
Dispatcher.Invoke(() => settingtime.Text = addr406Value.ToString("F0"));
}
}
catch (Exception ex)
{
//ShowErrorMsg($"读取地址406失败{ex.Message}");
}
}
private async System.Threading.Tasks.Task ReadAddr404DataAsync()
{
try
{
ushort[] registers = await System.Threading.Tasks.Task.Run(() =>
_modbusMaster.ReadHoldingRegisters(1, 1330, 2)
);
if (registers != null)
{
float addr406Value = c.UshortToFloat(registers[1], registers[0]);
Dispatcher.Invoke(() => min.Text = addr406Value.ToString("F2"));
}
}
catch (Exception ex)
{
//ShowErrorMsg($"读取地址406失败{ex.Message}");
}
}
private async System.Threading.Tasks.Task ReadAddr406DataAsync()
{
try
{
ushort[] registers = await System.Threading.Tasks.Task.Run(() =>
_modbusMaster.ReadHoldingRegisters(1, 72, 1)
);
if (registers != null)
{
int addr406Value = registers[0];
Dispatcher.Invoke(() => second.Text = addr406Value.ToString("F0"));
}
}
catch (Exception ex)
{
//ShowErrorMsg($"读取地址406失败{ex.Message}");
}
}
private async System.Threading.Tasks.Task ReadAddr130DataAsync()
{
try
{
ushort[] registers = await System.Threading.Tasks.Task.Run(() =>
_modbusMaster?.ReadHoldingRegisters(1, 76, 2)
);
if (registers != null)
{
double addr406Value = c.UshortToFloat(registers[1], registers[0]);
addr406Value = Math.Truncate(addr406Value * 100) / 100.0;
Dispatcher.Invoke(() =>
{
speed.Text = addr406Value.ToString("F2");
});
}
}
catch (Exception ex)
{
//ShowErrorMsg($"读取地址406失败{ex.Message}");
}
}
bool isReset = false;
// 地址400写入16位整数
private async void BtnWrite400_Click(object sender, RoutedEventArgs e)
{
_modbusMaster.WriteSingleCoil(1, 75, true);
isReset = true;
UpdateParameterButtonSelection(sender as Button);
}
private void ShowErrorMsg(string msg)
{
Dispatcher.Invoke(() =>
{
MessageBox.Show(msg, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
});
}
private static Brush CreateParameterButtonBrush(string colorCode)
{
return new SolidColorBrush((Color)ColorConverter.ConvertFromString(colorCode));
}
private void UpdateParameterButtonSelection(Button selectedButton)
{
if (selectedButton == null)
{
return;
}
if (_selectedParameterButton != null && !ReferenceEquals(_selectedParameterButton, selectedButton))
{
ApplyParameterButtonStyle(_selectedParameterButton, false);
}
_selectedParameterButton = selectedButton;
ApplyParameterButtonStyle(_selectedParameterButton, true);
}
private void ApplyParameterButtonStyle(Button button, bool isSelected)
{
if (button == null)
{
return;
}
button.Background = isSelected ? ParameterButtonSelectedBrush : ParameterButtonDefaultBrush;
button.BorderBrush = Brushes.Transparent;
button.BorderThickness = new Thickness(0);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//ma.BtnClickFunctionForNew(Function.ButtonType.切换型, 170);
OpenQuantitativeSupplyWindow2();
// SwitchWindow(ref _reportWindow, () => new ReportWindow());
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
ma.BtnClickFunctionForNew(Function.ButtonType., 70);
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
//ma.BtnClickFunctionForNew(Function.ButtonType.复归型, 73);
ma.BtnClickFunctionForNew(Function.ButtonType., 73);
}
private void Button_Click_3(object sender, RoutedEventArgs e)
{
ma.BtnClickFunctionForNew(Function.ButtonType., 74);
}
private void Button_Click_4(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow, () => new MainWindow(), "正在进入定量供应检验", "正在准备定量供应检验页面...");
}
private void Button_Click_5(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow2, () => new MainWindow2(), "正在进入负压气密性检验", "正在切换到负压气密性检验页面...");
}
private void Button_Click_6(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow3, () => new MainWindow3(), "正在进入正压气密性检验", "正在切换到正压气密性检验页面...");
}
private void Button_Click_7(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow4, () => new MainWindow4(), "正在进入排气阀压力测试", "正在切换到排气阀压力测试页面...");
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string plcIp = "192.168.1.10";
bool initSuccess = Data.ModbusResourceManager.Instance.Init(plcIp, 502);
if (!initSuccess)
{
MessageBox.Show("连接Modbus服务器失败", "错误");
this.Close();
return;
}
// 检查连接状态
if (_tcpClient == null || !_tcpClient.Connected)
{
MessageBox.Show("Modbus连接异常", "错误");
this.Close();
return;
}
ma = new Function(_modbusMaster);
_modbusMaster.WriteSingleCoil(1, 61, true);
System.Threading.Tasks.Task.Delay(50);
_modbusMaster.WriteSingleCoil(1, 62, false);
System.Threading.Tasks.Task.Delay(50);
_modbusMaster.WriteSingleCoil(1, 63, false);
System.Threading.Tasks.Task.Delay(50);
_modbusMaster.WriteSingleCoil(1, 64, false);
System.Threading.Tasks.Task.Delay(50);
_modbusMaster.WriteSingleCoil(1, 75, true);
_readTimer.Start();
pressureDiff2.Text = ConfigurationManager.AppSettings["No"]?.ToString();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
// 停止定时器
_readTimer?.Stop();
// 停止ViewModel的定时器
if (DataContext is MainViewModel viewModel)
{
viewModel.StopTimer();
}
// 释放Modbus资源
ModbusResourceManager.Instance?.Dispose();
// 延后到当前窗口完成关闭后再统一关停,避免关闭重入。
AppShutdownCoordinator.RequestShutdown();
}
private void Window_Closed(object sender, EventArgs e)
{
// 由应用级关停统一处理其他窗口,避免关闭链路重入。
}
private void BtnWrite401_Click(object sender, RoutedEventArgs e)
{
if (!short.TryParse(settingtime.Text.Trim(), out short writeValue) ||
writeValue < short.MinValue || writeValue > short.MaxValue)
{
ShowErrorMsg($"需输入整数(范围:{short.MinValue} ~ {short.MaxValue}");
settingtime.Focus();
return;
}
try
{
//await System.Threading.Tasks.Task.Run(() =>
// _modbusMaster.WriteSingleRegister(SlaveId, Address262, (ushort)writeValue)
//);
ma.WriteToPLCForNew(settingtime.Text.Trim(), 300, Function.DataType.);
System.Threading.Tasks.Task.Delay(50);
_isManualInput = false; // 写入后恢复读取
UpdateParameterButtonSelection(sender as Button);
//await ReadAddr400DataAsync(); // 刷新显示(确认写入成功)
}
catch (Exception ex)
{
ShowErrorMsg($"地址400写入失败{ex.Message}");
}
}
private void SwitchWindow<T>(ref T windowInstance, Func<T> createFunc, string loadingTitle = null, string loadingSubtitle = null) where T : Window, new()
{
// 1. 停止当前窗口的定时器(不释放资源)
_readTimer?.Stop();
// 2. 检查资源是否可用(添加重连机制)
if (_tcpClient == null || !_tcpClient.Connected || _modbusMaster == null)
{
// 尝试重新连接
bool reconnectSuccess = TryReconnect();
if (!reconnectSuccess)
{
MessageBox.Show("TCP连接已断开请重新连接", "提示");
return;
}
}
// 3. 复用窗口实例:不存在则创建,存在则激活
if (windowInstance == null)
{
windowInstance = createFunc();
// 添加窗口关闭事件处理
windowInstance.Closed += (s, args) =>
{
WindowNavigationHelper.RestoreWindow(this, () => _readTimer?.Start());
};
}
// 4. 目标窗口完成首帧渲染后再切换,避免白屏
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance, loadingTitle, loadingSubtitle);
}
// 添加重连方法
private bool TryReconnect()
{
try
{
string plcIp = "192.168.1.10";
bool initSuccess = Data.ModbusResourceManager.Instance.Init(plcIp, 502);
if (initSuccess)
{
ma = new Function(_modbusMaster);
return true;
}
}
catch (Exception ex)
{
ShowErrorMsg($"重新连接失败:{ex.Message}");
}
return false;
}
private void OpenQuantitativeSupplyWindow()
{
try
{
SwitchWindow(ref _mainWindow5, () => new ParameterSettingsWindow());
}
catch (Exception ex)
{
MessageBox.Show($"打开定量供应检验窗口时出错:{ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void btn_setting_Click(object sender, RoutedEventArgs e)
{
OpenQuantitativeSupplyWindow();
}
private async System.Threading.Tasks.Task ReadAddrStatusDataAsync()
{
try
{
bool[] result = _modbusMaster?.ReadCoils(0x01, 71, 1);
bool isTestRunning = result != null && result.Length > 0 && result[0];
Dispatcher.Invoke(() =>
{
if (isTestRunning)
{
status.Text = "正在运行";
statusPanel.Background = Brushes.Green;
if (!_wasRunning)
{
_testStartTime = DateTime.Now;
_recordAdded = false; // 测试刚开始,允许插入记录
}
}
else
{
status.Text = "空闲";
statusPanel.Background = Brushes.Blue;
// 状态从“运行”切换到“空闲”,且还没插入记录
if (_wasRunning && !_recordAdded)
{
AddTestRecord(); // 插入记录
_recordAdded = true; // 标记已插入
}
}
_wasRunning = isTestRunning; // 更新上一次状态
});
}
catch (Exception ex)
{
ShowErrorMsg($"读取运行状态失败:{ex.Message}");
}
}
private void OpenQuantitativeSupplyWindow2()
{
try
{
SwitchWindow(ref _reportWindow, () => new ReportWindow());
}
catch (Exception ex)
{
MessageBox.Show($"打开定量供应检验窗口时出错:{ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void AddTestRecord()
{
try
{
// 计算测试时长
TimeSpan duration = DateTime.Now - _testStartTime;
string durationStr = $"{duration.Minutes:00}:{duration.Seconds:00}";
// 获取当前流量值
double currentFlowRate = 0;
if (!string.IsNullOrEmpty(speed.Text) && double.TryParse(speed.Text, out double flow))
{
currentFlowRate = flow;
}
// 获取当前时间
string currentTime = DateTime.Now.ToString("HH:mm");
string currentDate = DateTime.Now.ToString("yy/MM/dd");
// 添加到报表窗口(如果存在或创建)
Dispatcher.Invoke(() =>
{
// 如果窗口不存在,先创建但不显示
if (_reportWindow == null)
{
_reportWindow = new ReportWindow();
}
double temprate = 0;
if (!isReset)
{
temprate = currentFlowRate;
currentFlowRate = 0;
}
else
{
}
string No = ConfigurationManager.AppSettings["No"]?.ToString();
// 添加记录到窗口(无论窗口是否显示)
_reportWindow.AddRecord(currentTime, currentDate, currentFlowRate, durationStr, temprate, No);
});
}
catch (Exception ex)
{
ShowErrorMsg($"记录测试数据失败:{ex.Message}");
}
}
private async void BtnWrite400_Click1(object sender, RoutedEventArgs e)
{
_modbusMaster.WriteSingleCoil(1, 75, false);
isReset = false;
UpdateParameterButtonSelection(sender as Button);
}
//private void pressureDiff2_GotFocus(object sender, RoutedEventArgs e)
//{
//}
private void pressureDiff2_GotFocus(object sender, RoutedEventArgs e)
{
SaveAppSetting("No", pressureDiff2.Text);
}
/// <summary>
/// 保存 AppSettings 配置到配置文件
/// </summary>
/// <param name="key">配置键名</param>
/// <param name="value">配置值</param>
/// <returns>是否保存成功</returns>
public bool SaveAppSetting(string key, string value)
{
try
{
// 1. 获取当前程序的配置文件路径(编译后的 exe.config
string configPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
// 2. 加载配置文件ExeConfigurationFileMap 指定配置路径)
ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap { ExeConfigFilename = configPath };
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
// 3. 处理 appSettings 节点
if (config.AppSettings.Settings[key] == null)
{
// 键不存在 → 新增配置
config.AppSettings.Settings.Add(key, value);
}
else
{
// 键已存在 → 更新配置
config.AppSettings.Settings[key].Value = value;
}
// 4. 保存配置(重要!否则修改不生效)
config.Save(ConfigurationSaveMode.Modified);
// 5. 刷新 ConfigurationManager 缓存(可选,让后续读取能立即获取新值)
ConfigurationManager.RefreshSection("appSettings");
return true;
}
catch (Exception ex)
{
// 捕获异常(如权限不足、文件被占用等)
MessageBox.Show($"保存配置失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
}
private void settingtime_TextChanged(object sender, TextChangedEventArgs e)
{
}
}
public class TestRecord
{
public int Id { get; set; }
public string Time { get; set; }//时间
public string Date { get; set; }//日期
public double FlowRate { get; set; } // 供氧流量L/min
public string Duration { get; set; } // 测试时长
public DateTime CreateTime { get; set; }
public string HeadName { get; set; } // 表头名
public double FlowRate2 { get; set; } // 供氧流量L/min
public double FlowRate3 { get; set; } // 供氧流量L/min
public string No { get; set; }
}
}