Files
hoodFieldOfView/头罩视野slove/头罩视野/Views/RecordDate.xaml.cs
2026-04-28 15:08:34 +08:00

356 lines
12 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 System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Timers;
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 .Services;
using .Services.Data;
using static .TestDataStore;
namespace .Views
{
/// <summary>
/// RecordDate.xaml 的交互逻辑
/// </summary>
///
public partial class RecordDate : Page
{
private IModbusMaster _modbusMaster => ModbusResourceManager.Instance.ModbusMaster;
private System.Timers.Timer? _plcReadTimer;
// 表跟数据存储列表
public List<dynamic> LeftEyeDataList = new List<dynamic>();
public List<dynamic> RightEyeDataList = new List<dynamic>();
public List<dynamic> CalLeftData = new List<dynamic>();
public List<dynamic> CalRightData = new List<dynamic>();
// 配置和你PLC地址完全对应 左目
private const int LeftEyeStartAddress = 1362; // D1362
private const int ChannelCount = 72; // 72个通道
private const int RegistersPerChannel = 1; // 每个通道2个寄存器Float
//右目
private const int RightEyeStartAddress = 1218; // D1218
//// 长按清除用
private bool _isClearPressed = false;
private Thread _clearThread;
public RecordDate()
{
InitializeComponent();
DynamicHeader();
// 2. 调用(名字和上面的变量一致)
// 2. 启动定时器定时读取数据每100ms读一次
StartPlcReadTimer(1000);
//// 判断连接
if (!ModbusHelper.IsConnected)
{
MessageBox.Show("未连接");
return;
}
}
//动态生成表头
void DynamicHeader()
{
// 2. 循环生成 72 个 ch 列
for (int i = 1; i <= ChannelCount; i++)
{
dataGrid1.Columns.Add(new DataGridTextColumn
{
Header = $"ch.{i}",
Binding = new System.Windows.Data.Binding($"Ch{i}")
});
dataGrid2.Columns.Add(new DataGridTextColumn
{
Header = $"ch.{i}",
Binding = new System.Windows.Data.Binding($"Ch{i}")
});
}
}
public void StopPlcTimer()
{
if (_plcReadTimer != null)
{
_plcReadTimer.Stop();
_plcReadTimer.Dispose();
_plcReadTimer = null;
}
}
//定时读取 PLC 数据
public void StartPlcReadTimer(int intervalMs)
{
// 防止重复创建
if (_plcReadTimer != null && _plcReadTimer.Enabled)
return;
_plcReadTimer = new System.Timers.Timer(intervalMs);
_plcReadTimer.Elapsed += ReadPlcData;
_plcReadTimer.Start();
}
private void ReadPlcData(object? sender, ElapsedEventArgs e)
{
// 左通道
ReadPlcDataGeneric(
slaveAddress: 1,
startAddress: LeftEyeStartAddress,
count: 72,
dataList: LeftEyeDataList,
dataGrid: dataGrid1);
//// 右通道
//ReadPlcDataGeneric(
// slaveAddress: 1,
// startAddress: RightEyeStartAddress,
// count: (ushort)(ChannelCount * RegistersPerChannel),
// dataList: RightEyeDataList,
// dataGrid: dataGrid2);
}
/// <summary>
/// 读取PLC HoldingRegisters 并更新到列表和DataGrid
/// <param name="slaveAddress">设备站号一般是1</param>
/// <param name="startAddress">起始地址</param>
/// <param name="count">寄存器总数</param>
/// <param name="dataList">数据缓存列表</param>
/// <param name="dataGrid">要更新的DataGrid控件</param>
/// 通用PLC读取方法左右通道通用
/// </summary>
private void ReadPlcDataGeneric(
byte slaveAddress,
ushort startAddress,
ushort count,
List<dynamic> dataList,
DataGrid dataGrid)
{
if (_modbusMaster == null || !ModbusHelper.TcpClient.Connected)
return;
try
{
// 读取寄存器
ushort[] registers = _modbusMaster.ReadHoldingRegisters(
slaveAddress: slaveAddress,
startAddress: startAddress,
numberOfPoints: (ushort)count);
uint[] data32 = ConvertRegistersToUInt32(registers, true);
// 先把变量存为局部变量,解决闭包问题
var regCopy = data32;
var listCopy = dataList;
var gridCopy = dataGrid;
// 交给UI线程更新数据和表格
Dispatcher.Invoke(() =>
{
AddPlcDataRow(regCopy, listCopy, gridCopy);
});
System.Diagnostics.Debug.WriteLine("读取寄存器数据" );
}
catch (Exception ex)
{
Console.WriteLine($"PLC读取失败: {ex.Message}");
}
}
private uint[] ConvertRegistersToUInt32(ushort[] registers, bool isLittleEndian)
{
if (registers == null || registers.Length % 2 != 0)
throw new ArgumentException("寄存器数量必须是偶数");
uint[] result = new uint[registers.Length / 2];
for (int i = 0; i < result.Length; i++)
{
ushort low = registers[i * 2];
ushort high = registers[i * 2 + 1];
if (isLittleEndian)
result[i] = (uint)(low | (high << 16));
else
result[i] = (uint)(high | (low << 16));
}
return result;
}
/// <summary>
/// 把PLC数据添加到动态表格
private int _rowIndex = 1;
private void AddPlcDataRow(uint[] registers, List<dynamic> dataList, DataGrid dg)
{
// 1. 先清空临时列表,不影响历史数据
//dataList.Clear();
// 2. 构建一次采集的单行数据(包含所有通道)
dynamic newRow = new System.Dynamic.ExpandoObject();
var dict = (IDictionary<string, object>)newRow;
// 固定列:编号、时间、日期(每次采集的这一行)
dict["Id"] = _rowIndex++;
dict["Time"] = DateTime.Now.ToString("HH:mm:ss");
dict["Date"] = DateTime.Now.ToString("yyyy-MM-dd");
// 关键:循环给所有通道赋值,一行里包含所有通道值
for (int i = 0; i < registers.Length; i++)
{
// 字段名和你界面列绑定的名称必须完全一致,比如 ch.1 / Ch1
dict[$"Ch{i + 1}"] = registers[i];
//dict[$"ch.{i + 1}"] = registers[i];
}
// 3. 把这一行加到历史列表,实现“读多行”
dataList.Add(newRow);
// 4. 强制刷新DataGrid让界面显示多行
dg.Dispatcher.Invoke(() =>
{
dg.ItemsSource = null;
dg.ItemsSource = dataList;
dg.Items.Refresh(); // 强制刷新视图,避免不渲染
});
}
//面积的计算方法
public void getAllData(List<dynamic> leftEyeDataList, List<dynamic> RightEyeDataList, int perAngle)
{
// 1. 先去除异常值,生成新列表(不修改原列表)
var filteredLeft = GetArea.RemoveOutliers(leftEyeDataList);
var filteredRight = GetArea.RemoveOutliers(RightEyeDataList);
//左目视野面积
GlobalData.LeftEyeArea = GetArea.CalculateEyeArea(filteredLeft);
//右目视野面积
GlobalData.RightEyeArea = GetArea.CalculateEyeArea(filteredRight);
//双目视野面积
GlobalData.BinocularArea = GetArea.CalcBinocularArea(filteredLeft, filteredRight);
//// 总视野面积
GlobalData.TotalEyeArea = GlobalData.LeftEyeArea + GlobalData.RightEyeArea - GlobalData.BinocularArea;
//// 空白视野面积
GlobalData.BlankArea = GetArea.StandardTotal - GlobalData.TotalEyeArea;
//视野保存率
// 左眼平均值数组
double[] leftAvg = GetArea.GetEyeAvgArray(filteredLeft);
// 右眼平均值数组
double[] rightAvg = GetArea.GetEyeAvgArray(RightEyeDataList);
double leftLowerAngle = GetArea.CalcLowerAngle(leftAvg, perAngle);
double rightLowerAngle = GetArea.CalcLowerAngle(rightAvg, perAngle);
//下方视野
GlobalData.LowerVision = Math.Min(leftLowerAngle, rightLowerAngle);
//视野保存率
GlobalData.VisionRetentionRate = GetArea.CalcVisionRate(GlobalData.LeftEyeArea, GlobalData.RightEyeArea);
//打印数值显示在系统上面
System.Diagnostics.Debug.WriteLine("左目视野面积" + GlobalData.LeftEyeArea);
System.Diagnostics.Debug.WriteLine("右目视野面积" + GlobalData.RightEyeArea);
System.Diagnostics.Debug.WriteLine("双目视野面积" + GlobalData.BinocularArea);
System.Diagnostics.Debug.WriteLine("总视野面积" + GlobalData.TotalEyeArea);
System.Diagnostics.Debug.WriteLine("下方视野" + GlobalData.LowerVision);
System.Diagnostics.Debug.WriteLine("视野保存率" + GlobalData.VisionRetentionRate);
}
//#endregion
// 保存左眼
private void btnSaveLeft_Click(object sender, RoutedEventArgs e)
{
//SaveToCsv(LeftEyeDataList, $"左眼数据_{DateTime.Now:yyyyMMddHHmmss}.csv");
ModbusHelper.SaveToCsv(LeftEyeDataList, $"左眼数据_{DateTime.Now:yyyyMMddHHmmss}.csv");
}
// 保存右眼
private void btnSaveRight_Click(object sender, RoutedEventArgs e)
{
ModbusHelper.SaveToCsv(RightEyeDataList,$"右眼数据_{DateTime.Now:yyyyMMddHHmmss}.csv");
}
//清除
private void btnClear_MouseDown(object sender, MouseButtonEventArgs e)
{
//_isClearPressed = true;
//_clearThread = new Thread(() =>
//{
// Thread.Sleep(500); // 长按1秒触发
// if (_isClearPressed)
// {
// Application.Current.Dispatcher.Invoke(() => ClearAllData());
// }
//});
//_clearThread.Start();
_plcReadTimer?.Stop();
}
// 清除所有数据
private void ClearAllData()
{
LeftEyeDataList.Clear();
dataGrid1.Items.Clear();
RightEyeDataList.Clear();
dataGrid2.Items.Clear();
MessageBox.Show("数据已清除");
}
private void btnClear_MouseUp(object sender, MouseButtonEventArgs e)
{
_isClearPressed = false;
_clearThread?.Join(100); // 等待线程结束最多100毫秒然后强制结束
}
private void Page_Unloaded(object sender, RoutedEventArgs e)
{
_plcReadTimer?.Stop();
_plcReadTimer?.Dispose();
//_modbusMaster?.Dispose();
//ModbusHelper.TcpClient?.Close();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
}
private void GoHome(object s, RoutedEventArgs e) => NavigationService.Content = null;
private void GoTest(object s, RoutedEventArgs e) => 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();
//NavigationService.Navigate(new Views.RecordDate()); 页面相互跳转
}
}