using Microsoft.Win32; using Modbus.Device; using Sunny.UI; //using RecordDateView; using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; 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 头罩视野.Services; using 头罩视野.Services.Data; using static 头罩视野.TestDataStore; namespace 头罩视野.Views { public partial class PageTest : Page { /// 只加这一个变量 private CancellationTokenSource? _cts; private IModbusMaster _modbusMaster => ModbusResourceManager.Instance.ModbusMaster; DispatcherTimer _timer; DataChange c = new DataChange(); Function ma; bool isFinished; //// 定时采集用 private DispatcherTimer testTimer; // 保存上一条数据(用于去重) private TestDataStore.TestRecord? _lastRecord; private object btnLeftEye; // 表跟数据存储列表 public List DataList = new List(); public PageTest() { InitializeComponent(); System.Diagnostics.Debug.WriteLine("页面加载了!111111111"); _timer = InitDispatcherTimer(); // 2. 初始化定时器:500毫秒 执行一次 testTimer = new DispatcherTimer(); testTimer.Interval = TimeSpan.FromMilliseconds(500); // 500ms = 0.5秒 testTimer.Tick += Timer_Tick; //// 判断连接 if (!ModbusHelper.IsConnected) { MessageBox.Show("未连接"); return; } } // // 蓝色亮(蓝色) private void LedOn(Ellipse led) { led.Fill = Brushes.LightSkyBlue; } // 灯灭(灰色) private void LedOff(Ellipse led) { led.Fill = Brushes.LightGray; } //复位btn private void Button_Click_Reset(object sender, RoutedEventArgs e) { btnLeft.Content = "左眼开"; btnRight.Content = "右眼开"; LedOff(led1); LedOff(led0); ma.BtnClickFunction(Function.ButtonType.复归型, 90); } //左开眼 private void Button_Click_left(object sender, RoutedEventArgs e) { // 切换文案:开是关,关是开 if (btnLeft.Content.ToString() == "左眼开") { btnLeft.Content = "左眼关"; LedOn(led0); if (btnRight.Content.ToString() == "右眼关") { btnRight.Content = "右眼开"; LedOff(led1); ma.BtnClickFunction(Function.ButtonType.切换型, 1); } } else { btnLeft.Content = "左眼开"; LedOff(led0); } ma.BtnClickFunction(Function.ButtonType.切换型, 0); //LedOn(led0); } //右开眼 private void Button_Click_Right(object sender, RoutedEventArgs e) { // 切换文案:左眼开 ↔ 左眼关 if (btnRight.Content.ToString() == "右眼开") { btnRight.Content = "右眼关"; LedOn(led1); if (btnLeft.Content.ToString() == "左眼关") { btnLeft.Content = "左眼开"; LedOff(led0); ma.BtnClickFunction(Function.ButtonType.切换型, 0); } } else { btnRight.Content = "右眼开"; LedOff(led1); } ma.BtnClickFunction(Function.ButtonType.切换型, 1); //LedOn(led1); } //反转 private async void Button_Click_ResDown(object sender, MouseButtonEventArgs e) { await _modbusMaster.WriteSingleCoilAsync(1, 10, true); System.Diagnostics.Debug.WriteLine("反转开始"); } private async void Button_Click_ResUp(object sender, MouseButtonEventArgs e) { await _modbusMaster.WriteSingleCoilAsync(1, 10, false); System.Diagnostics.Debug.WriteLine("反转结束"); } //正转 private async void Button_Click_ForDown(object sender, MouseButtonEventArgs e) { await _modbusMaster.WriteSingleCoilAsync(1, 11, true); } private async void Button_Click_ForUp(object sender, MouseButtonEventArgs e) { await _modbusMaster.WriteSingleCoilAsync(1, 11, false); //System.Diagnostics.Debug.WriteLine("正传end"); } // 在 PageTest 类里面 private RecordDate? GetRecordDatePage() { // 拿到主窗口 var mainWin = Application.Current.MainWindow; // 假设你全局切换页面的 Frame 名字叫 MainFrame var frame = mainWin.FindName("MainFrame") as Frame; // 返回 RecordDate 实例 return frame?.Content as RecordDate; } //测试btn 测试按钮:读取数据,存入共享列表 private void Button_Click_Test(object sender, RoutedEventArgs e) { ma.BtnClickFunction(Function.ButtonType.复归型, 100); ButtonTest.Content = "测试中...."; testTimer.Start(); } //停止btn private void Button_Click_Stop(object sender, RoutedEventArgs e) { ma.BtnClickFunction(Function.ButtonType.复归型, 103); ButtonTest.Content = "测试"; testTimer.Stop(); } private async void Timer_Tick(object sender, EventArgs e) { System.Diagnostics.Debug.WriteLine("定时器触发了!" + DateTime.Now); // 调用你原来的读取方 //await ReadAddr262DataAsync(); // 组装当前数据 var data = new TestDataStore.TestRecord { Date = DateTime.Now.ToString("yyyy-MM-dd"), Time = DateTime.Now.ToString("HH:mm:ss"), LeftEyeArea = double.TryParse(zmsyarea.Text, out var l) ? l : 0, RightEyeArea = double.TryParse(ymsyarea.Text, out var r) ? r : 0, BinocularArea = double.TryParse(smsyarea.Text, out var b) ? b : 0, LowerVision = double.TryParse(xfsyarea.Text, out var lv) ? lv : 0, VisionRetentionRate = double.TryParse(sybhl.Text, out var vr) ? vr : 0 }; // ==================== 去重判断 ==================== if (_lastRecord != null && data.LeftEyeArea == _lastRecord.LeftEyeArea && data.RightEyeArea == _lastRecord.RightEyeArea && data.BinocularArea == _lastRecord.BinocularArea && data.LowerVision == _lastRecord.LowerVision && data.VisionRetentionRate == _lastRecord.VisionRetentionRate) { return; // 一样就不添加 } //原来存的数据清空 切换页面会清空 //TestDataStore.Records.Clear(); // 不一样 → 插入表格 TestDataStore.AddNewRecord(data); _lastRecord = data; } //打印 private void Button_Click_Print(object sender, RoutedEventArgs e) { ma.BtnClickFunction(Function.ButtonType.复归型, 103); } //试样测试 private void TbTest_Checked(object sender, RoutedEventArgs e) { // 选中 → 试样测试 tbTest.Content = "空白测试"; tbTest.Background = System.Windows.Media.Brushes.LightSkyBlue; System.Diagnostics.Debug.WriteLine($"1232312312"); _modbusMaster.WriteSingleCoilAsync(1, 41, true); } private void TbTest_Unchecked(object sender, RoutedEventArgs e) { // 取消 → 空白测试 tbTest.Content = "试样测试"; tbTest.Background = System.Windows.Media.Brushes.LightGray; _modbusMaster.WriteSingleCoilAsync(1, 41, false); } private void Button_Click_home(object sender, RoutedEventArgs e) { NavigationService.Content = null; } //读取数据 private DispatcherTimer InitDispatcherTimer() { var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(500) }; timer.Tick += async (s, e) => { if (_modbusMaster != null) { try { await ReadAddr262DataAsync(); } catch { } } }; return timer; } private List<(int m, int n)> _lightPositions; private async System.Threading.Tasks.Task ReadAddr262DataAsync() { try { // 创建任务列表 var tasks = new List { //ReadAndUpdateFloatAsync(200, 2, fbspeed, "F2", "°"), //ReadAndUpdateFloatAsync(202, 2, dqangle, "F2", "°"), //ReadAndUpdateFloatAsync(204, 2, zmsyarea, "F2", "cm²"), //ReadAndUpdateFloatAsync(206 ,2, xfsyarea, "F2", " "), //ReadAndUpdateFloatAsync(208, 2, smsyarea, "F2", "cm²"), //ReadAndUpdateFloatAsync(210 ,2, ymsyarea, "F2", " "), ReadAndUpdateFloatRangeAsync(200, 12, "F2", "°"), //ReadAndUpdateFloatAsync(424 ,2, kbsyarea, "F2", "cm²"), ReadAndUpdateFloatAsync(310, 2, zdangle, "F2", "°/S"), //ReadAndUpdateFloatAsync(430 ,2, sybhl, "F2", " "), //前1从站地址,后1是长度 ReadLightBarData() }; isFinished = _modbusMaster.ReadCoils(1, 102, 1)[0]; if (isFinished) { Button_Click_Stop(null, null); //开始计算视野面积 // 1. 把DataList(List)转成方法需要的int[] int[] lightData = DataList.Cast().ToArray(); //2. 调用CalculateEllipseArea,传入两个参数 double area = GetArea.CalculateEllipseArea(lightData, _lightPositions); //值显示在页面, if (btnRight.Content.ToString() == "右眼开"&& btnLeft.Content.ToString() == "左眼关") { zmsyarea.Text = area.ToString("0.00");//左目 } else if (btnRight.Content.ToString() == "右眼关" && btnLeft.Content.ToString() == "左眼开") { ymsyarea.Text = area.ToString("0.00"); //右目 } else if(btnRight.Content.ToString() == "右眼关" && btnLeft.Content.ToString() == "左眼关") { smsyarea.Text = area.ToString("0.00"); // 双目 } // 空白 double binocularTotalArea = double.Parse(smsyarea.Text); double blankArea = GetArea.GetBlankViewArea(binocularTotalArea); kbsyarea.Text = blankArea.ToString("0.00"); // 保留2位小数 double BottomViewAngle = GetArea.CalculateEllipseArea(lightData, _lightPositions); xfsyarea.Text = BottomViewAngle.ToString("0.00"); // 下方 double binocularRate = GetArea.CalcVisionRate(binocularTotalArea); sybhl.Text = binocularRate.ToString("0.00"); // 视野保存率 } await Task.WhenAll(tasks); } catch (Exception ex) { ShowError($"读取数据失败:{ex.Message}"); } } // 地址, 根据格式显示字符长度 32:2 16:1 ,绑定页面的name 值,F2 保留两位小数,单位 private async Task ReadAndUpdateFloatAsync(int address, int length, System.Windows.Controls.TextBox control, string format, string unit) { try { // 1. 页面销毁时直接退出,不执行 if (_cts?.IsCancellationRequested == true) return; // 2. 关键:判断对象为空就直接返回,不执行 if (_modbusMaster == null) return; ushort[] registers = await Task.Run(async () => await _modbusMaster!.ReadHoldingRegistersAsync(1, (ushort)address, (ushort)length) ); if (registers != null && registers.Length >= 2) { float value = c.UshortToFloat(registers[1], registers[0]); Dispatcher.Invoke(() => control.Text = value.ToString(format) + unit); } else if (registers != null && registers.Length >= 1) { int value = registers[0]; Dispatcher.Invoke(() => control.Text = value.ToString(format) + unit); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"读取地址{address}失败:{ex.Message}"); } } private async Task ReadAndUpdateFloatRangeAsync(int address, int length, string format, string unit) { try { ushort[] registers = await Task.Run(async () => await _modbusMaster!.ReadHoldingRegistersAsync(1, (ushort)address, (ushort)length) ); if (registers != null && registers.Length >= 2) { float value = c.UshortToFloat(registers[1], registers[0]); float value2 = c.UshortToFloat(registers[3], registers[2]); float value3 = c.UshortToFloat(registers[5], registers[4]); //float value4 = c.UshortToFloat(registers[7], registers[6]); //float value5 = c.UshortToFloat(registers[9], registers[8]); //float value6 = c.UshortToFloat(registers[11], registers[10]); Dispatcher.Invoke(() => { fbspeed.Text = value.ToString(format) + unit; dqangle.Text = value2.ToString(format) + unit; //zmsyarea.Text = value3.ToString(format) + unit; //xfsyarea.Text = value4.ToString(format) + unit; //smsyarea.Text = value5.ToString(format) + unit; //ymsyarea.Text = value6.ToString(format) + unit; //control.Text = value.ToString(format) + unit); }); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"读取地址{address}失败:{ex.Message}"); } } //写入 private void fbspeed_GotFocus(object sender, RoutedEventArgs e) { ma.WriteToPLCForNew(fbspeed.Text.Trim(), 200, Function.DataType.浮点型); //程序等待50s 在进行 System.Threading.Tasks.Task.Delay(50); fbspeed.Focus(); } private void dqangle_GotFocus(object sender, RoutedEventArgs e) { ma.WriteToPLCForNew(dqangle.Text.Trim(), 202, Function.DataType.浮点型); System.Threading.Tasks.Task.Delay(50); dqangle.Focus(); } private void zdangle_GotFocus(object sender, RoutedEventArgs e) { ma.WriteToPLCForNew(zdangle.Text.Trim(), 310, Function.DataType.浮点型); System.Threading.Tasks.Task.Delay(50); zdangle.Focus(); } //错误信息提示 private void ShowError(string msg) => MessageBox.Show(msg, "错误", MessageBoxButton.OK, MessageBoxImage.Error); private void GoHome(object s, RoutedEventArgs e) => NavigationService.Content = null; private void GoTest(object s, RoutedEventArgs e) { _timer.Stop(); NavigationService.Content = new Views.PageTest(); } private void GoRecord(object s, RoutedEventArgs e) => NavigationService.Content = new Views.RecordDate(); private void GoView(object s, RoutedEventArgs e) => NavigationService.Content = new Views.RecordPage(); private void Page_Loaded(object sender, RoutedEventArgs e) { System.Diagnostics.Debug.WriteLine("页面加载了!112222222"); _timer.Start(); ma = new Function(_modbusMaster); c = new DataChange(); //zmsyarea.Text = "4.00"; // 左目 //smsyarea.Text = "5.00"; // 双目 //kbsyarea.Text = "6.00"; // 空白 //ymsyarea.Text = "7.00"; // 右目 //xfsyarea.Text = "8.00"; // 下方 //sybhl.Text = "9.00"; // 视野保存率 } private void Page_Unloaded(object sender, RoutedEventArgs e) { testTimer?.Stop(); _timer?.Stop(); _cts?.Cancel(); _cts = null; } //读取灯泡的数据 /// /// 读取灯条PLC数据 → 自动存入 DataList /// 可直接放入 Task 列表等待 /// private async Task ReadLightBarData() { // 无连接直接返回 if (_modbusMaster == null || !ModbusHelper.TcpClient.Connected) return; try { // 读取灯条寄存器(地址350,长度15) ushort[] registers = await _modbusMaster.ReadHoldingRegistersAsync(1, 350, 15); // 每个 ushort 是16位,低位在前 foreach (ushort reg in registers) { for (int bit = 0; bit < 16; bit++) { // 取第bit位的值:0或1 int lightBit = (reg & (1 << bit)) != 0 ? 1 : 0; DataList.Add(lightBit); } } // 调试:打印转换后的数据长度 System.Diagnostics.Debug.WriteLine($"灯条二进制数据总长度:{DataList.Count}"); } catch (Exception ex) { Console.WriteLine($"灯条数据读取失败:{ex.Message}"); DataList.Clear(); // 出错时清空数据 } } } }