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-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个通道
|
|
|
|
|
|
private const int RegistersPerChannel = 2; // 每个通道2个寄存器(Float)
|
|
|
|
|
|
|
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-24 15:43:33 +08:00
|
|
|
|
//StartPlcReadTimer(100);
|
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,
|
|
|
|
|
|
count: (ushort)(ChannelCount * RegistersPerChannel),
|
|
|
|
|
|
dataList: LeftEyeDataList,
|
|
|
|
|
|
dataGrid: dataGrid1);
|
|
|
|
|
|
|
|
|
|
|
|
// 右通道
|
|
|
|
|
|
ReadPlcDataGeneric(
|
|
|
|
|
|
slaveAddress: 1,
|
|
|
|
|
|
startAddress: RightEyeStartAddress,
|
|
|
|
|
|
count: (ushort)(ChannelCount * RegistersPerChannel),
|
|
|
|
|
|
dataList: RightEyeDataList,
|
|
|
|
|
|
dataGrid: dataGrid2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 16:45:06 +08:00
|
|
|
|
|
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,
|
|
|
|
|
|
numberOfPoints: count);
|
|
|
|
|
|
|
|
|
|
|
|
// 先把变量存为局部变量,解决闭包问题
|
|
|
|
|
|
var regCopy = registers;
|
|
|
|
|
|
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
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
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-24 14:55:20 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 把PLC数据添加到动态表格
|
|
|
|
|
|
/// </summary>
|
2026-04-27 17:41:15 +08:00
|
|
|
|
private void AddPlcDataRow(ushort[] registers, List<dynamic> dataList, DataGrid dg)
|
2026-04-22 13:37:39 +08:00
|
|
|
|
{
|
2026-04-24 17:47:59 +08:00
|
|
|
|
|
2026-04-24 14:55:20 +08:00
|
|
|
|
// 清空旧数据,防止重复
|
|
|
|
|
|
dataList.Clear();
|
2026-04-22 13:37:39 +08:00
|
|
|
|
|
2026-04-24 14:55:20 +08:00
|
|
|
|
// 把PLC数据存入列表
|
|
|
|
|
|
dataList.AddRange(registers);
|
2026-04-22 13:37:39 +08:00
|
|
|
|
|
2026-04-24 14:55:20 +08:00
|
|
|
|
// 构建动态行(适配你原来的ExpandoObject逻辑)
|
|
|
|
|
|
List<dynamic> rows = new List<dynamic>();
|
|
|
|
|
|
for (int i = 0; i < registers.Length; i++)
|
2026-04-22 13:37:39 +08:00
|
|
|
|
{
|
2026-04-24 14:55:20 +08:00
|
|
|
|
dynamic row = new System.Dynamic.ExpandoObject();
|
|
|
|
|
|
var dict = (IDictionary<string, object>)row;
|
|
|
|
|
|
|
|
|
|
|
|
// 固定列:编号、时间、数值
|
|
|
|
|
|
dict["Id"] = i + 1;
|
|
|
|
|
|
dict["Time"] = DateTime.Now.ToString("HH:mm:ss");
|
|
|
|
|
|
dict["Value"] = registers[i];
|
|
|
|
|
|
|
|
|
|
|
|
rows.Add(row);
|
2026-04-22 13:37:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-24 14:55:20 +08:00
|
|
|
|
// 更新DataGrid
|
|
|
|
|
|
dg.ItemsSource = null;
|
|
|
|
|
|
dg.ItemsSource = rows;
|
2026-04-24 17:47:59 +08:00
|
|
|
|
|
2026-04-24 14:55:20 +08:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 16:45:06 +08:00
|
|
|
|
//面积的计算方法
|
2026-04-27 17:41:15 +08:00
|
|
|
|
public void getAllData(List<dynamic> leftEyeDataList, List<dynamic> RightEyeDataList, double 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-27 16:45:06 +08:00
|
|
|
|
GlobalData.LeftEyeArea = GetArea.CalculateEyeArea(leftEyeDataList);
|
2026-04-24 14:55:20 +08:00
|
|
|
|
//右目视野面积
|
2026-04-27 16:45:06 +08:00
|
|
|
|
GlobalData.RightEyeArea = GetArea.CalculateEyeArea(RightEyeDataList);
|
2026-04-24 14:55:20 +08:00
|
|
|
|
//双目视野面积
|
2026-04-27 16:45:06 +08:00
|
|
|
|
GlobalData.BinocularArea = GetArea.CalcBinocularArea(leftEyeDataList, RightEyeDataList);
|
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-27 16:45:06 +08:00
|
|
|
|
double[] leftAvg = GetArea.GetEyeAvgArray(leftEyeDataList);
|
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)
|
|
|
|
|
|
{
|
|
|
|
|
|
_isClearPressed = true;
|
|
|
|
|
|
_clearThread = new Thread(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
Thread.Sleep(500); // 长按1秒触发
|
|
|
|
|
|
if (_isClearPressed)
|
|
|
|
|
|
{
|
|
|
|
|
|
Application.Current.Dispatcher.Invoke(() => ClearAllData());
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
_clearThread.Start();
|
|
|
|
|
|
}
|
|
|
|
|
|
// 清除所有数据
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|