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.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using 自救器呼吸器综合检验仪.Data;
namespace 自救器呼吸器综合检验仪
{
///
/// MainWindow.xaml 的交互逻辑
///
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; // 测试开始时间
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;
}
private void ShowErrorMsg(string msg)
{
Dispatcher.Invoke(() =>
{
MessageBox.Show(msg, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
});
}
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";
//string plcIp = "127.0.0.1";
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();
string imagePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources/sleep2.jpg");
ImageBrush brush = new ImageBrush();
brush.ImageSource = new BitmapImage(new Uri(imagePath, UriKind.Absolute));
this.Background = brush;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
// 停止定时器
_readTimer?.Stop();
// 停止ViewModel的定时器
if (DataContext is MainViewModel viewModel)
{
viewModel.StopTimer();
}
// 释放Modbus资源
ModbusResourceManager.Instance?.Dispose();
// 确保应用程序完全退出
Application.Current.Shutdown();
}
private void Window_Closed(object sender, EventArgs e)
{
// 清理其他窗口实例
//_mainWindow?.Close();
_mainWindow2?.Close();
_mainWindow3?.Close();
_mainWindow4?.Close();
_mainWindow5?.Close();
_reportWindow?.Close();
}
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; // 写入后恢复读取
//await ReadAddr400DataAsync(); // 刷新显示(确认写入成功)
}
catch (Exception ex)
{
ShowErrorMsg($"地址400写入失败:{ex.Message}");
}
}
private void SwitchWindow(ref T windowInstance, Func createFunc) where T : Window, new()
{
// 停止当前窗口的定时器(不释放资源)
_readTimer?.Stop();
// 检查资源是否可用(添加重连机制)
if (_tcpClient == null || !_tcpClient.Connected || _modbusMaster == null)
{
if (!TryReconnect())
{
MessageBox.Show("TCP连接已断开,请重新连接!", "提示");
return;
}
}
// 创建一个简易的加载覆盖窗口,作为状态栏/遮罩,避免白屏
Window overlay = null;
try
{
overlay = new Window
{
Owner = this,
WindowStyle = WindowStyle.None,
AllowsTransparency = true,
// 取消遮罩背景色,使用完全透明背景以避免暗色遮罩
Background = Brushes.Transparent,
ShowInTaskbar = false,
ResizeMode = ResizeMode.NoResize,
Width = this.ActualWidth,
Height = this.ActualHeight,
Left = this.Left,
Top = this.Top,
ShowActivated = false,
Topmost = true
};
// 内容:底部居中的状态文本(不遮挡背景)
var grid = new Grid();
var tb = new TextBlock
{
Text = "正在加载,请稍候...",
Foreground = Brushes.Black,
Background = Brushes.Transparent,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Bottom,
Margin = new Thickness(0, 0, 0, 20),
FontSize = 16
};
grid.Children.Add(tb);
overlay.Content = grid;
// 显示遮罩
overlay.Show();
void ShowTargetWindow(T target)
{
// 如果目标已可见,直接激活并隐藏当前
if (target.IsVisible)
{
target.Activate();
this.Hide();
overlay?.Close();
return;
}
target.Opacity = 0;
void OnContentRendered(object s, EventArgs e)
{
target.ContentRendered -= OnContentRendered;
var anim = new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(200));
target.BeginAnimation(Window.OpacityProperty, anim);
this.Hide();
target.Activate();
overlay?.Close();
}
target.ContentRendered += OnContentRendered;
target.Show();
}
// 创建或复用窗口实例
if (windowInstance == null)
{
windowInstance = createFunc();
windowInstance.Closed += (s, args) =>
{
_readTimer?.Start();
this.Activate();
};
ShowTargetWindow(windowInstance);
}
else
{
ShowTargetWindow(windowInstance);
}
}
catch (Exception ex)
{
overlay?.Close();
ShowErrorMsg($"切换窗口失败:{ex.Message}");
}
}
// 添加重连方法
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;
}
//private void pressureDiff2_GotFocus(object sender, RoutedEventArgs e)
//{
//}
private void pressureDiff2_GotFocus(object sender, RoutedEventArgs e)
{
SaveAppSetting("No", pressureDiff2.Text);
}
///
/// 保存 AppSettings 配置到配置文件
///
/// 配置键名
/// 配置值
/// 是否保存成功
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)
{
}
private void Button_Click_8(object sender, RoutedEventArgs 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; }
}
}