Files
hoodFieldOfView/头罩视野slove/头罩视野/Views/PageTest.xaml.cs
2026-05-06 09:21:35 +08:00

616 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 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 double _leftTotalArea = 0; // 左目总视野面积
private double _rightTotalArea = 0; // 右目总视野面积
private double _binocularTotalArea = 0; // 双目总视野面积
// 表跟数据存储列表
public List<dynamic> DataList = new List<dynamic>();
public PageTest()
{
InitializeComponent();
InitLightPositions();
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 const int LightsPerStrip = 81; // 单条灯条81个灯
private const int HalfLights = (LightsPerStrip - 1) / 2; // 40给左右灯条用
private List<(int m, int n)> _lightPositions;
private void InitLightPositions()
{
// 清空列表,避免重复初始化
_lightPositions = new List<(int m, int n)>();
for (int m = 0; m < LightsPerStrip; m++)
{
_lightPositions.Add((m, n: 0));
}
for (int m = 0; m < LightsPerStrip; m++)
{
_lightPositions.Add((m, n: 1));
}
for (int m = -HalfLights; m <= HalfLights; m++)
{
_lightPositions.Add((m, n: 2));
}
// 验证总数81+81+81=243和硬件总灯数完全一致
System.Diagnostics.Debug.WriteLine($"灯条数据:{_lightPositions.Count}");
}
// 蓝色亮(蓝色)
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");
}
//停止btn
private void Button_Click_Stop(object sender, RoutedEventArgs e)
{
ma.BtnClickFunction(Function.ButtonType., 103);
ButtonTest.Content = "测试";
testTimer.Stop();
}
//测试btn 测试按钮:读取数据,存入共享列表
private void Button_Click_Test(object sender, RoutedEventArgs e)
{
ma.BtnClickFunction(Function.ButtonType., 100);
ButtonTest.Content = "测试中....";
testTimer.Start();
double stepAngle;
double.TryParse(fbspeed.Text.Trim(), out double val);
// 范围判断:必须在 0 ~ 180 之间
if (val <= 0 || val > 180)
{
stepAngle = 10.0; // 超出范围用默认值
}
else
{
stepAngle = val; // 正常就用输入值
}
// 1. 读取输入框
// 分辨角度 例10
bool isLeftOnly = btnLeft.Content.ToString() == "左眼开" && btnRight.Content.ToString() == "右眼关";
bool isRightOnly = btnRight.Content.ToString() == "右眼开" && btnLeft.Content.ToString() == "左眼关";
bool isBinocular = btnLeft.Content.ToString() == "左眼开" && btnRight.Content.ToString() == "右眼开";
if (isLeftOnly)
{ _leftTotalArea = 0;
}
else if (isRightOnly)
{
_rightTotalArea = 0;
}
else if (isBinocular)
{
_binocularTotalArea = 0;
}
double maxBottomViewAngle = 0; ;//记录所有姿态里的最大下方视野
// 2. 从0转到180每 stepAngle 执行一次
for (double current = stepAngle; current <= 180; current += stepAngle)
{
int[] lightData = DataList.Cast<int>().ToArray();
_ = ReadLightBarData();
//开始计算视野面积
double singleArea = GetArea.CalculateEllipseArea(lightData, _lightPositions);
if (isLeftOnly)
{
_leftTotalArea += singleArea;
}
else if (isRightOnly)
{
_rightTotalArea += singleArea;
}
else if (isBinocular)
{
_binocularTotalArea += singleArea;
}
// 单次计算下方视野角度
double bottomViewAngle = GetArea.CalculateBottomViewAngle(lightData, _lightPositions);
// 记录所有姿态里的最大/最小值
if (bottomViewAngle > maxBottomViewAngle)
{
maxBottomViewAngle = bottomViewAngle;
}
// 如果你要取最小值,用下面这段
// if (bottomViewAngle < minBottomViewAngle && bottomViewAngle > 0)
// {
// minBottomViewAngle = bottomViewAngle;
// }
}
isFinished = _modbusMaster.ReadCoils(1, 102, 1)[0];
if (isFinished)
{
Button_Click_Stop(null, null);
UpdateVisionResults( maxBottomViewAngle);
}
}
//页面渲染值
public void UpdateVisionResults( double BotViAn)
{
zmsyarea.Text = _leftTotalArea.ToString("0.00"); // 左目
ymsyarea.Text = _rightTotalArea.ToString("0.00"); // 右目
smsyarea.Text = _binocularTotalArea.ToString("0.00"); // 双目
// 3. 计算空白区视野面积(双目时才有效)
if (double.TryParse(smsyarea.Text, out double binocularTotalArea))
{
double blankArea = GetArea.GetBlankViewArea(binocularTotalArea);
kbsyarea.Text = blankArea.ToString("0.00"); // 空白视野面积
}
//// 4. 计算下方视野面积
int botViAnInt = (int)Math.Round(BotViAn);
xfsyarea.Text = botViAnInt.ToString("0.0"); // 下方视野
// 5. 计算视野保存率(双目)
if (double.TryParse(smsyarea.Text, out double totalAreaForRate))
{
double binocularRate = GetArea.CalcVisionRate(totalAreaForRate);
sybhl.Text = binocularRate.ToString("0.00"); // 视野保存率
}
}
//读取灯泡的数据
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(); // 出错时清空数据
}
}
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 async System.Threading.Tasks.Task ReadAddr262DataAsync()
{
try
{
// 创建任务列表
var tasks = new List<Task>
{
//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是长度
};
await Task.WhenAll(tasks);
}
catch (Exception ex)
{
ShowError($"读取数据失败:{ex.Message}");
}
}
// 地址, 根据格式显示字符长度 322 161 绑定页面的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;
}
}
}