页面逻辑添加
This commit is contained in:
@@ -164,37 +164,80 @@ public static class ModbusHelper
|
||||
//double total = left + right - binocular;
|
||||
|
||||
|
||||
//下方视野面积
|
||||
|
||||
public static double CalcLowerEyeArea(
|
||||
List<double[]> eyeGroups,
|
||||
double threshold,
|
||||
double standardLowerArea)
|
||||
//下方视野角度
|
||||
/// <summary>
|
||||
/// GB2890-2022 计算 单眼下方视野角度
|
||||
/// eyeData:单眼72路平均数据数组
|
||||
/// threshold:有效亮度阈值
|
||||
/// </summary>
|
||||
public static double CalcLowerAngle(double[] eyeData, double threshold = 10)
|
||||
{
|
||||
// 1. 每个通道求20组平均
|
||||
// 总72点 每点5°
|
||||
int totalPoint = 72;
|
||||
double perAngle = 5;
|
||||
|
||||
// 国标:最下方起始点位(第54号开始为正下方)
|
||||
int startDownIndex = 54;
|
||||
|
||||
int validCount = 0;
|
||||
|
||||
// 从最下方向上 连续检测有效点
|
||||
for (int i = 0; i < 36; i++)
|
||||
{
|
||||
int idx = (startDownIndex + i) % totalPoint;
|
||||
|
||||
if (eyeData[idx] >= threshold)
|
||||
{
|
||||
validCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 断开直接停止
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 下方视野角度 = 有效点数 × 单步角度
|
||||
return validCount * perAngle;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 计算单眼72点通道平均值数组
|
||||
/// </summary>
|
||||
/// <param name="eyeGroups">多组采样数据集合</param>
|
||||
/// <returns>72点通道平均值数组</returns>
|
||||
private static double[] GetEyeAvgArray(List<double[]> eyeGroups)
|
||||
{
|
||||
if (eyeGroups == null || eyeGroups.Count == 0)
|
||||
return new double[72]; // 无数据时返回全0数组
|
||||
|
||||
double[] avg = new double[72];
|
||||
|
||||
for (int i = 0; i < 72; i++)
|
||||
{
|
||||
double sum = 0;
|
||||
foreach (var g in eyeGroups) sum += g[i];
|
||||
foreach (var group in eyeGroups)
|
||||
{
|
||||
sum += group[i];
|
||||
}
|
||||
avg[i] = sum / eyeGroups.Count;
|
||||
}
|
||||
|
||||
// 2. 只统计下半 36 个通道(下方视野)
|
||||
int validCount = 0;
|
||||
for (int i = 36; i < 72; i++) // 36~71 是下半区
|
||||
{
|
||||
if (avg[i] >= threshold)
|
||||
validCount++;
|
||||
}
|
||||
|
||||
// 3. 计算下方视野面积
|
||||
return (validCount / 36.0) * standardLowerArea;
|
||||
return avg;
|
||||
}
|
||||
//下方视野计算方法
|
||||
////1. 先拿到左右眼 72点平均数组
|
||||
//double[] leftAvg = GetLeftEyeAvgArray();
|
||||
//double[] rightAvg = GetRightEyeAvgArray();
|
||||
|
||||
// 总下方视野面积调用
|
||||
//double totalLower = leftLower + rightLower - binocularLower;
|
||||
////2. 分别算下方角度
|
||||
//double leftLowerAngle = CalcLowerAngle(leftAvg, 10);
|
||||
//double rightLowerAngle = CalcLowerAngle(rightAvg, 10);
|
||||
|
||||
////3. 最终报告取值(国标取双眼较小值)
|
||||
//double finalLowerAngle = Math.Min(leftLowerAngle, rightLowerAngle);
|
||||
//bool lowerAngleOk = finalLowerAngle >= 35;
|
||||
|
||||
//空白视野面积计算
|
||||
|
||||
@@ -236,5 +279,128 @@ public static class ModbusHelper
|
||||
|
||||
|
||||
|
||||
//double totalSaveRate = VisionCalculator.CalculateVisionSaveRate(totalVisionArea, standardTotalArea);
|
||||
}
|
||||
// 空头模(无面罩)标定的标准面积(GB2890-2022)
|
||||
//public static double StandardSingleEye = 5200;
|
||||
//// 双眼总标准面积(左+右)
|
||||
//public static double StandardTotalEye = 10400;
|
||||
//// 双目重叠标准面积
|
||||
////public static double StandardBinocular = 4200;
|
||||
//// 下方视野标准角度
|
||||
//public static double StandardLowerAngle = 75;
|
||||
//===== 1. 你预先标定的 空模标准面积 =====
|
||||
// 单眼标准、总标准(左+右)、双目重叠标准
|
||||
public static double StandardLeftEye = 5180;
|
||||
|
||||
public static double StandardRightEye = 5180;
|
||||
|
||||
public static double StandardTotal = 10360;
|
||||
|
||||
|
||||
public static double StandardBinocular = 4150;
|
||||
|
||||
//===== 2. 传入你采集的实测面积 =====
|
||||
// leftArea:左眼实测 rightArea:右眼实测 binArea:双目重叠实测
|
||||
public static double CalcVisionRate(double leftArea, double rightArea)
|
||||
{
|
||||
// 总视野实测 = 左+右
|
||||
double totalSi = leftArea + rightArea;
|
||||
|
||||
// 1. 总视野保存率
|
||||
double ratioTotal = totalSi / StandardTotal;
|
||||
double gammaTotal = GetVisionGamma(ratioTotal);
|
||||
double totalRate = gammaTotal * ratioTotal * 100;
|
||||
return (totalRate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GB2890-2022 自动获取 总视野/双目视野 校正系数γ
|
||||
/// </summary>
|
||||
/// <param name="ratio">实测面积/标准面积 比值(0~1)</param>
|
||||
/// <returns>校正系数 γ</returns>
|
||||
public static double GetVisionGamma(double ratio)
|
||||
{
|
||||
// X:视野残存率 Si/S0
|
||||
double[] xData = { 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 };
|
||||
|
||||
// 总视野 γ 对应值
|
||||
double[] gammaTotal = { 1.22, 1.18, 1.14, 1.10, 1.06, 1.03, 1.02, 1.01, 1.00 };
|
||||
|
||||
double[] yData = gammaTotal;
|
||||
|
||||
// 边界限制
|
||||
if (ratio <= xData[0]) return yData[0];
|
||||
if (ratio >= xData.Last()) return 1.0;
|
||||
|
||||
// 线性插值
|
||||
for (int i = 0; i < xData.Length - 1; i++)
|
||||
{
|
||||
if (ratio >= xData[i] && ratio <= xData[i + 1])
|
||||
{
|
||||
double t = (ratio - xData[i]) / (xData[i + 1] - xData[i]);
|
||||
return yData[i] + t * (yData[i + 1] - yData[i]);
|
||||
}
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
internal static double CalculateEyeArea(List<dynamic> leftEyeDataList, int v1, int v2)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal static double CalcBinocularArea(List<dynamic> leftEyeDataList, List<dynamic> rightEyeDataList, int v1, int v2)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal static double[] GetEyeAvgArray(List<dynamic> leftEyeDataList)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
// 你算出来的实际面积
|
||||
//double left = 4250;
|
||||
//double right = 4320;
|
||||
//double bin = 2860;
|
||||
|
||||
//// 一键算出 国标保存率
|
||||
//var result = CalcVisionRate(left, right, bin);
|
||||
|
||||
//double 总视野保存率 = result.totalRate;
|
||||
//double 双目视野保存率 = result.binRate;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
//// 1. 总视野保存率
|
||||
//double gammaTotal = 查国标图D.4的总视野γ;
|
||||
//double totalRate = gammaTotal * totalArea / GlobalData.StandardTotalEye * 100;
|
||||
|
||||
// // 2. 双目视野保存率
|
||||
// double gammaBinoc = 查国标图D.4的双目视野γ;
|
||||
//double binocRate = gammaBinoc * binocArea / GlobalData.StandardBinocular * 100;
|
||||
|
||||
//// 3. 下方视野(直接比角度,不用面积)
|
||||
//bool lowerPass = lowerAngle >= 35;
|
||||
|
||||
|
||||
|
||||
// 四、关键澄清(你之前问的)
|
||||
//下方视野:国标是角度(°),不是面积
|
||||
//按左右眼视野曲线下方交点位置直接读出角度
|
||||
//合格:≥35°
|
||||
|
||||
//总视野 = 左眼 + 右眼(国标明确)
|
||||
|
||||
//双目视野 = 左右眼重叠部分(单独算面积)
|
||||
|
||||
//五、最简总结(国标一句话)
|
||||
|
||||
//总视野保存率 =(γ ×(左 + 右实测面积))/ 标准总面积 ×100%
|
||||
|
||||
//双目视野保存率 =(γ × 重叠实测面积)/ 标准重叠面积 ×100%
|
||||
|
||||
//下方视野:直接看角度 ≥35°
|
||||
//}
|
||||
@@ -44,5 +44,18 @@ namespace 头罩视野
|
||||
record.Id = _nextId++;
|
||||
Records.Add(record);
|
||||
}
|
||||
|
||||
|
||||
public static class GlobalData
|
||||
{
|
||||
// 要传的所有数据放这里
|
||||
public static double LeftEyeArea { get; set; }
|
||||
public static double RightEyeArea { get; set; }
|
||||
public static double TotalEyeArea { get; set; }
|
||||
public static double BinocularArea { get; set; }
|
||||
public static double BlankArea { get; set; }
|
||||
public static double LowerVision { get; set; }//下方视野角度
|
||||
public static double VisionRetentionRate { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using 头罩视野.Services.Data;
|
||||
using static 头罩视野.TestDataStore;
|
||||
namespace 头罩视野.Views
|
||||
{
|
||||
/// <summary>
|
||||
@@ -32,11 +33,16 @@ namespace 头罩视野.Views
|
||||
private List<dynamic> LeftEyeDataList = new List<dynamic>();
|
||||
private List<dynamic> RightEyeDataList = new List<dynamic>();
|
||||
|
||||
// 配置:和你PLC地址完全对应
|
||||
// 配置:和你PLC地址完全对应 左目
|
||||
private const int LeftEyeStartAddress = 1362; // D1362
|
||||
private const int ChannelCount = 72; // 72个通道
|
||||
private const int RegistersPerChannel = 2; // 每个通道2个寄存器(Float)
|
||||
|
||||
//右目
|
||||
|
||||
private const int RightEyeStartAddress = 1218; // D1218
|
||||
|
||||
|
||||
// 长按清除用
|
||||
private bool _isClearPressed = false;
|
||||
private Thread _clearThread;
|
||||
@@ -83,64 +89,158 @@ namespace 头罩视野.Views
|
||||
}
|
||||
|
||||
private void ReadPlcData(object? sender, ElapsedEventArgs e)
|
||||
{
|
||||
// 左通道
|
||||
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);
|
||||
}
|
||||
|
||||
private void ReadPlcDataGeneric(int slaveAddress, int startAddress, ushort count, List<dynamic> dataList, DataGrid dataGrid)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
/// <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<ushort> dataList,
|
||||
DataGrid dataGrid)
|
||||
{
|
||||
if (_modbusMaster == null || !ModbusHelper.TcpClient.Connected)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// 读取D1362开始的128个寄存器(64个通道×2) 参数定义:PLC 设备号默认1,数据起始地址D1362 要读的寄存器总数
|
||||
// 读取寄存器
|
||||
ushort[] registers = _modbusMaster.ReadHoldingRegisters(
|
||||
slaveAddress: 1, // 设备站,默认1
|
||||
startAddress: LeftEyeStartAddress,
|
||||
numberOfPoints: ChannelCount * RegistersPerChannel
|
||||
);
|
||||
slaveAddress: slaveAddress,
|
||||
startAddress: startAddress,
|
||||
numberOfPoints: count);
|
||||
|
||||
// 把寄存器转换成浮点数,并添加到表格
|
||||
// 先把变量存为局部变量,解决闭包问题
|
||||
var regCopy = registers;
|
||||
var listCopy = dataList;
|
||||
var gridCopy = dataGrid;
|
||||
|
||||
// 交给UI线程更新数据和表格
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
AddPlcDataRow(registers, LeftEyeDataList, dataGrid1);
|
||||
AddPlcDataRow(regCopy, listCopy, gridCopy);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 读取失败不报错,避免程序崩溃
|
||||
Console.WriteLine($"PLC读取错误:{ex.Message}");
|
||||
Console.WriteLine($"PLC读取失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
//把 PLC 数据添加到你的动态表格
|
||||
|
||||
private void AddPlcDataRow(ushort[] registers, List<dynamic> dataList, DataGrid dg)
|
||||
/// <summary>
|
||||
/// 把PLC数据添加到动态表格
|
||||
/// </summary>
|
||||
private void AddPlcDataRow(ushort[] registers, List<ushort> dataList, DataGrid dg)
|
||||
{
|
||||
dynamic row = new System.Dynamic.ExpandoObject();
|
||||
var dict = (IDictionary<string, object>)row;
|
||||
// 清空旧数据,防止重复
|
||||
dataList.Clear();
|
||||
|
||||
// 固定列:编号、时间、日期
|
||||
dict["Id"] = dataList.Count + 1;
|
||||
dict["Time"] = DateTime.Now.ToString("HH:mm:ss");
|
||||
dict["Date"] = DateTime.Now.ToString("yyyy-MM-dd");
|
||||
// 把PLC数据存入列表
|
||||
dataList.AddRange(registers);
|
||||
|
||||
// 通道列:把寄存器转成Float
|
||||
for (int i = 0; i < ChannelCount; i++)
|
||||
// 构建动态行(适配你原来的ExpandoObject逻辑)
|
||||
List<dynamic> rows = new List<dynamic>();
|
||||
for (int i = 0; i < registers.Length; i++)
|
||||
{
|
||||
// 每个Float占2个寄存器
|
||||
ushort high = registers[i * 2];
|
||||
ushort low = registers[i * 2 + 1];
|
||||
dynamic row = new System.Dynamic.ExpandoObject();
|
||||
var dict = (IDictionary<string, object>)row;
|
||||
|
||||
// 组合成32位浮点数
|
||||
byte[] bytes = new byte[4];
|
||||
bytes[0] = (byte)(low & 0xFF);
|
||||
bytes[1] = (byte)(low >> 8);
|
||||
bytes[2] = (byte)(high & 0xFF);
|
||||
bytes[3] = (byte)(high >> 8);
|
||||
float value = BitConverter.ToSingle(bytes, 0);
|
||||
// 固定列:编号、时间、数值
|
||||
dict["Id"] = i + 1;
|
||||
dict["Time"] = DateTime.Now.ToString("HH:mm:ss");
|
||||
dict["Value"] = registers[i];
|
||||
|
||||
dict[$"Ch{i + 1}"] = Math.Round(value, 2); // 保留2位小数
|
||||
rows.Add(row);
|
||||
}
|
||||
|
||||
// 加到列表和表格
|
||||
dataList.Add(row);
|
||||
dg.Items.Add(row);
|
||||
// 更新DataGrid
|
||||
dg.ItemsSource = null;
|
||||
dg.ItemsSource = rows;
|
||||
//根据不的dg 值 可以算出 左/右目视野面积
|
||||
|
||||
}
|
||||
|
||||
//private void AddPlcDataRow()
|
||||
//{
|
||||
// AddPlcDataRow(LeftEyeDataList);
|
||||
//}
|
||||
|
||||
|
||||
//左右目面积调用方法
|
||||
private void AddPlcDataRow(List<dynamic> leftEyeDataList, List<dynamic> RightEyeDataList)
|
||||
{ //左目视野面积
|
||||
GlobalData.LeftEyeArea = ModbusHelper.CalculateEyeArea(leftEyeDataList,
|
||||
80,
|
||||
120
|
||||
);
|
||||
//右目视野面积
|
||||
GlobalData.RightEyeArea = ModbusHelper.CalculateEyeArea(RightEyeDataList,
|
||||
80,
|
||||
120
|
||||
);
|
||||
//双目视野面积
|
||||
GlobalData.BinocularArea = ModbusHelper.CalcBinocularArea(leftEyeDataList, RightEyeDataList, 80, 120);
|
||||
|
||||
//// 总视野面积
|
||||
GlobalData.TotalEyeArea = GlobalData.LeftEyeArea + GlobalData.RightEyeArea - GlobalData.BinocularArea;
|
||||
|
||||
//// 空白视野面积
|
||||
GlobalData.BlankArea = 120 - GlobalData.TotalEyeArea;
|
||||
|
||||
//视野保存率
|
||||
|
||||
// 左眼平均值数组
|
||||
double[] leftAvg = ModbusHelper.GetEyeAvgArray(leftEyeDataList);
|
||||
|
||||
// 右眼平均值数组
|
||||
double[] rightAvg = ModbusHelper.GetEyeAvgArray(RightEyeDataList);
|
||||
|
||||
double leftLowerAngle = ModbusHelper.CalcLowerAngle(leftAvg, 10);
|
||||
double rightLowerAngle = ModbusHelper.CalcLowerAngle(rightAvg, 10);
|
||||
|
||||
GlobalData.LowerVision = Math.Min(leftLowerAngle, rightLowerAngle);
|
||||
|
||||
//视野保存率
|
||||
GlobalData.VisionRetentionRate = ModbusHelper.CalcVisionRate(GlobalData.LeftEyeArea, GlobalData.RightEyeArea);
|
||||
|
||||
}
|
||||
|
||||
private double CalcLowerAngle(double[] leftAvg, int v)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
@@ -201,7 +301,7 @@ namespace 头罩视野.Views
|
||||
}
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -212,4 +312,50 @@ namespace 头罩视野.Views
|
||||
|
||||
//NavigationService.Navigate(new Views.RecordDate()); 页面相互跳转
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//// 公开定时器(让别的页面能访问)
|
||||
//public System.Timers.Timer PlcReadTimer;
|
||||
|
||||
// // 公开:启动定时器
|
||||
// public void StartRecordTimer()
|
||||
// {
|
||||
// if (PlcReadTimer == null)
|
||||
// {
|
||||
// PlcReadTimer = new System.Timers.Timer(100);
|
||||
// PlcReadTimer.Elapsed += ReadPlcData; // 你的PLC读取方法
|
||||
// PlcReadTimer.Start();
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 公开:停止定时器
|
||||
// public void StopRecordTimer()
|
||||
// {
|
||||
// if (PlcReadTimer != null)
|
||||
// {
|
||||
// PlcReadTimer.Stop();
|
||||
// PlcReadTimer.Dispose();
|
||||
// PlcReadTimer = null;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 你的PLC读取方法
|
||||
// private void ReadPlcData(object sender, System.Timers.ElapsedEventArgs e)
|
||||
// {
|
||||
// // 你原来的逻辑
|
||||
// }
|
||||
|
||||
|
||||
// 找到 RecordDate 页面实例 页面调用和停止
|
||||
//var recordPage = Application.Current.MainWindow
|
||||
// .FindName("MainFrame") as Frame
|
||||
// ?.Content as RecordDate;
|
||||
|
||||
//// 找到了就调用
|
||||
//if (recordPage != null)
|
||||
//{
|
||||
// recordPage.StartRecordTimer(); // 启动
|
||||
// // recordPage.StopRecordTimer(); // 停止
|
||||
//}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user