Files
hoodFieldOfView/头罩视野slove/头罩视野/Views/RecordDate.xaml.cs

356 lines
12 KiB
C#
Raw Normal View History

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