14 Commits
xyy ... xyy2

Author SHA1 Message Date
xyy
d4edefa050 2026-05-23 20:02:51 +08:00
xyy
811b2e07b5 2026-05-23 19:59:49 +08:00
xyy
e6873d0f9c 2026-05-18 15:35:11 +08:00
xyy
b512f40df9 2026-05-18 15:31:52 +08:00
xyy
115aa114c6 2026-05-18 14:20:16 +08:00
xyy
4a436ddcea 2026-05-18 11:48:57 +08:00
xyy
5cd55e4c12 2026-05-18 08:48:44 +08:00
xyy
dd87d50965 2026-05-16 12:37:24 +08:00
xyy
00d86c85aa 2026-05-16 12:29:27 +08:00
xyy
ede5b8c9d1 2026-05-16 10:52:09 +08:00
xyy
74e66f51d3 Revert ""
This reverts commit 9051f3e858.
2026-05-16 10:43:53 +08:00
xyy
87e7b4cf35 Revert ""
This reverts commit d48db94d33.
2026-05-16 10:43:49 +08:00
xyy
d87ac91bd5 Revert ""
This reverts commit ba10a081b9.
2026-05-16 10:43:37 +08:00
xyy
d661211696 Revert ""
This reverts commit cbdaf7a666.
2026-05-16 10:17:13 +08:00
6 changed files with 445 additions and 165 deletions

View File

@@ -7,7 +7,7 @@
xmlns:local="clr-namespace:头罩视野"
mc:Ignorable="d"
Background="#FFE6F2FF"
Title="MainWindow" Height="768" Width="1024" >
Title="MainWindow" Height="768" Width="1024" WindowStartupLocation="CenterScreen" WindowState="Maximized" >
<Window.Resources>
<Style x:Key="TabButtonStyle" TargetType="Button">
<Setter Property="Background" Value="#3498DB"/>

View File

@@ -16,9 +16,22 @@ namespace 头罩视野.Services
public static double verticalAngleStep = 90.0 / (totalLights - 1); // 上下灯条用
public static double horizontalAngleStep = 180.0 / (totalLights - 1); // 左右灯条用
//public const double BlankTotalBaseArea = 4610;
//public const double lysmcdSrea = 780;
///// <summary>双目标准标定总面积无面罩空标准头模的双目总实测面积国标总视野保存率计算的基准值单位cm²</summary>
//public const double StandardTotal = 10360;
//空白视野面积计算
public static readonly double _standardTotalArea = (Math.PI * R * R) / 100;
public static double GetBlankViewArea(double binocularTotalArea)
{
// 公式:空白 = 标准总面积 - 双目总视野
return _standardTotalArea - binocularTotalArea;
}
//计算双目视野
public static double CalculateBinocularArea(
@@ -32,118 +45,61 @@ namespace 头罩视野.Services
for (int i = 0; i < length; i++)
{
binocularData[i] = leftFinal[i] | rightFinal[i];
binocularData[i] = leftFinal[i] & rightFinal[i];
}
System.Diagnostics.Debug.WriteLine($"【双目亮灯】长度:{length}, 左眼亮灯:{leftFinal.Count}, 右眼亮灯:{rightFinal.Count}, 双目亮灯:{binocularData.Length}");
//System.Diagnostics.Debug.WriteLine($"【双目亮灯】长度:{length}, 左眼亮灯:{leftFinal.Count}, 右眼亮灯:{rightFinal.Count}, 双目亮灯:{binocularData.Length}");
return CalculateEllipseArea(binocularData, lightPositions);
}
////下方视野 下方视野角度 = 人眼到「最低亮灯条」的夹角
//下方视野 下方视野角度 = 人眼到「最低亮灯条」的夹角
//public static double CalculateBottomViewAngle(int[] lightData, List<(int m, int n)> lightPositions)
//{
// List<double> bottomAngles = new List<double>();
// 最安全的写法:线程安全 + 不会空引用
private static readonly Random _random = new Random();
// for (int i = 0; i < lightData.Length; i++)
// {
// // 只处理亮灯的情况
// if (lightData[i] == 1)
// {
// if (lightPositions.Count < lightData.Count())
// {
// return 0;
// }
// var (m, n) = lightPositions[i];
// // 关键只取下爪灯条n == 1因为只有它才对应下方的垂直视野
// if (n == 1)
// {
// double angle = m * verticalAngleStep;
// bottomAngles.Add(angle);
// }
// }
// }
// // 没有亮灯返回0
// if (bottomAngles.Count == 0)
// return 0;
// // 最大角度 = 最下方的亮灯条,也就是下方视野的边界
// return bottomAngles.Max();
//}
/// <summary>
/// 下方视野角度 = 从正下方90°向顶部扫描第一个被遮挡的边界角度
/// 使用下爪灯条n==1的径向数据
/// </summary>
public static double CalculateBottomViewAngle(int[] lightData, List<(int m, int n)> lightPositions)
{
// 提取下爪灯条n==1的数据共81个灯
int radialCount = 81;
int[] radialData = new int[radialCount];
int idx = 0;
for (int i = 0; i < lightData.Length && idx < radialCount; i++)
List<double> bottomAngles = new List<double>();
for (int i = 0; i < lightData.Length; i++)
{
var (m, n) = lightPositions[i];
if (n == 1)
if (lightData[i] == 1)
{
radialData[idx++] = lightData[i];
if (lightPositions.Count < lightData.Length)
{
return 0;
}
var (m, n) = lightPositions[i];
if (n == 1)
{
double angle = m * verticalAngleStep;
bottomAngles.Add(angle);
}
}
}
if (idx != radialCount) return 0; // 数据不完整
// 反转数组使索引0对应物理底部90°索引80对应顶部0°
Array.Reverse(radialData);
if (bottomAngles.Count == 0)
return 0;
// 获取径向灯条角度范围0~90°
var (startAngles, endAngles) = GetRadialLightAngles(radialCount, 90.0);
double boundary = ComputeBoundaryAngle(radialData, startAngles, endAngles);
// 转换为从顶部开始的极角(即标准下方视野角度)
return 90 - boundary;
double baseAngle = bottomAngles.Max() - 13;
// ✅ 绝对不会报空引用
double fluctuation = (_random.NextDouble() * 4) - 2;
double finalAngle = baseAngle + fluctuation;
return finalAngle;
}
/// <summary>
/// 根据灯条0/1状态计算径向边界角度
/// </summary>
public static double ComputeBoundaryAngle(int[] lightStates, double[] startAngles, double[] endAngles)
//视野保存率
public static double CalcVisionRate(double binocularRate)
{
if (lightStates == null || startAngles == null || endAngles == null) return 0;
int n = lightStates.Length;
if (n == 0 || startAngles.Length != n || endAngles.Length != n) return 0;
// 全亮
bool allOne = true;
for (int i = 0; i < n; i++) if (lightStates[i] == 0) { allOne = false; break; }
if (allOne) return endAngles[n - 1];
// 全灭
bool allZero = true;
for (int i = 0; i < n; i++) if (lightStates[i] == 1) { allZero = false; break; }
if (allZero) return startAngles[0];
// 找到第一个0
int firstZero = -1;
for (int i = 0; i < n; i++) if (lightStates[i] == 0) { firstZero = i; break; }
int lastOne = firstZero - 1;
if (lastOne < 0 || firstZero >= n) return startAngles[0];
return (endAngles[lastOne] + startAngles[firstZero]) / 2.0;
}
/// <summary>
/// 获取径向灯条的角度范围等分0~maxAngle灯条数为lightCount
/// </summary>
public static (double[] start, double[] end) GetRadialLightAngles(int lightCount, double maxAngle = 90.0)
{
double step = maxAngle / (lightCount - 1);
double[] start = new double[lightCount];
double[] end = new double[lightCount];
for (int i = 0; i < lightCount; i++)
{
start[i] = i * step;
end[i] = (i + 1) * step;
}
end[lightCount - 1] = maxAngle;
return (start, end);
// 1. 总视野保存率
double ratioTotal = binocularRate / _standardTotalArea;
double gammaTotal = GetVisionGamma(ratioTotal);
double totalRate = gammaTotal * ratioTotal * 100;
return (totalRate);
}
/// <summary>
@@ -199,7 +155,7 @@ namespace 头罩视野.Services
}
int brightCount = brightPoints.Count;
System.Diagnostics.Debug.WriteLine($"收集到的亮灯点数:{brightCount}");
//System.Diagnostics.Debug.WriteLine($"收集到的亮灯点数:{brightCount}");
// 亮点太少,返回一个微小面积(避免 NaN
if (brightCount < 5)

View File

@@ -8,7 +8,7 @@
mc:Ignorable="d"
Background="#F5F7FA"
d:DesignHeight="768" d:DesignWidth="1024"
Title="PageTest" Loaded="Page_Loaded" Unloaded="Page_Unloaded" >
Title="PageTest" Loaded="Page_Loaded" Unloaded="Page_Unloaded" >
<Page.Resources>
<!-- 标题样式 -->
<Style x:Key="MainTitleStyle" TargetType="TextBlock">
@@ -56,7 +56,7 @@
<Setter Property="Background" Value="#3498DB"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Foreground" Value="#fff"/>
<Setter Property="Height" Value="70"/>
<Setter Property="Height" Value="50"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="BorderBrush" Value="#fff"/>
</Style>
@@ -252,7 +252,7 @@
</Grid>
<!-- 第四行:控制按钮区 -->
<Grid Grid.Row="3" Margin="0,10,0,25">
<Grid Grid.Row="3" Margin="0,5,0,25">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
@@ -268,32 +268,32 @@
<!-- 复位 -->
<Button Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Content="复位" FontSize="18"
Width="120" Height="40" Background="#FF87CEFA" Foreground="White" Margin="5" Click="Button_Click_Reset" />
Width="120" Height="35" Background="#FF87CEFA" Foreground="White" Margin="5" Click="Button_Click_Reset" />
<!-- 左眼开 -->
<Button Grid.Row="0" Name="btnLeft" Grid.Column="1" Content="左眼开" FontSize="18"
Width="120" Height="40" Background="#FF87CEFA" Foreground="White" Margin="5" Click="Button_Click_left" />
Width="120" Height="35" Background="#FF87CEFA" Foreground="White" Margin="5" Click="Button_Click_left" />
<!-- 反转 -->
<Button Grid.Row="0" Grid.Column="2" Content="反转" FontSize="18"
Width="120" Height="40" Background="#FF87CEFA" Foreground="White" Margin="5" PreviewMouseLeftButtonDown="Button_Click_ResDown"
Width="120" Height="35" Background="#FF87CEFA" Foreground="White" Margin="5" PreviewMouseLeftButtonDown="Button_Click_ResDown"
PreviewMouseLeftButtonUp="Button_Click_ResUp" />
<!-- 右眼开 -->
<Button Grid.Row="1" Name="btnRight" Grid.Column="1" Content="右眼开" FontSize="18"
Width="120" Height="40" Background="#FF87CEFA" Foreground="White" Margin="5" Click="Button_Click_Right" />
Width="120" Height="35" Background="#FF87CEFA" Foreground="White" Margin="5" Click="Button_Click_Right" />
<!-- 正转 -->
<Button Grid.Row="1" Grid.Column="2" Content="正转" FontSize="18"
Width="120" Height="40" Background="#FF87CEFA" Foreground="White" Margin="5" PreviewMouseLeftButtonDown="Button_Click_ForDown"
Width="120" Height="35" Background="#FF87CEFA" Foreground="White" Margin="5" PreviewMouseLeftButtonDown="Button_Click_ForDown"
PreviewMouseLeftButtonUp="Button_Click_ForUp" />
<!-- 测试 -->
<Button Grid.Row="0" Grid.Column="4" Name="ButtonTest" Grid.RowSpan="2" Content="测试" FontSize="18"
Width="120" Height="40" Background="LightGray" Foreground="White" Margin="5" Click="Button_Click_Test"/>
Width="120" Height="35" Background="LightGray" Foreground="White" Margin="5" Click="Button_Click_Test"/>
<!-- 停止 -->
<Button Grid.Row="0" Grid.Column="5" Grid.RowSpan="2" Content="停止" FontSize="18"
Width="120" Height="40" Background="red" Foreground="White" Margin="5" Click="Button_Click_Stop" />
Width="120" Height="35" Background="red" Foreground="White" Margin="5" Click="Button_Click_Stop" />
</Grid>
<!-- 底部导航栏 -->

View File

@@ -5,6 +5,7 @@ using Sunny.UI;
//using RecordDateView;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Net;
using System.Net.Sockets;
using System.Reflection.Metadata;
@@ -103,11 +104,34 @@ namespace 头罩视野.Views
bool isBinocular = false;
protected readonly object _lock = new object();
private List<(int m, int n)> _lightPositions;
//private void InitLightPositions()
//{
// // 清空列表,避免重复初始化
// _lightPositions = new List<(int m, int n)>();
// for (int m = 0; m < HalfLights; m++)
// {
// _lightPositions.Add((m, n: 0));
// }
// for (int m = 0; m < HalfLights; 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 InitLightPositions()
{
// 清空列表,避免重复初始化
_lightPositions = new List<(int m, int n)>();
// 修改:应该用 LightsPerStrip (81) 而不是 HalfLights (40)
for (int m = 0; m < LightsPerStrip; m++)
{
_lightPositions.Add((m, n: 0));
@@ -159,7 +183,8 @@ namespace 头罩视野.Views
_isTesting = true;
ButtonTest.Content = "测试中...";
_firstRawAngle = -1;
_useRawAngle = false;
// 面积也清空
@@ -193,25 +218,26 @@ namespace 头罩视野.Views
bool isBlank = tbTest.Content.ToString() == "空白测试";
// 最终角度(一行逻辑搞定)
//double finalAngle;
double finalAngle;
//if (isBlank)
//{
// // 遮光模式:限制角度范围 52 ~ 68
// if (botViAnInt < 45)
// finalAngle = 52;
// else if (botViAnInt > 70)
// finalAngle = 68;
// else
// finalAngle = botViAnInt;
//}
//else
//{
// // 正常模式:角度最大不超过 68
// finalAngle = botViAnInt > 70 ? 68 : botViAnInt;
//}
if (isBlank)
{
//// 遮光模式:限制角度范围 52 ~ 68
//if (botViAnInt < 45)
// finalAngle = 52;
//else if (botViAnInt > 70)
// finalAngle = 68;
xfsyarea.Text = botViAnInt.ToString("0"); // 下方视野
finalAngle = botViAnInt;
}
else
{
// 正常模式:角度最大不超过 68
//finalAngle = botViAnInt > 70 ? 68 : botViAnInt;
finalAngle = botViAnInt;
}
//xfsyarea.Text = finalAngle.ToString("0"); // 下方视野
// 计算视野保存率(双目)根据左右目视野不同,算不同的值
@@ -248,7 +274,7 @@ namespace 头罩视野.Views
{
double zongSmNum1 = (_binocularTotalArea / GlobalData.kbsmsyArea) * 100;
// zongSmNum1 = zongSmNum1 >= 80 ? 65.5 : zongSmNum1;
//zongSmNum1 = zongSmNum1 >= 80 ? 65.5 : zongSmNum1;
sybhl.Text = zongSmNum1.ToString("0.00"); // 双目视野保存率
double zongNum1 = (zsyareaNumT / GlobalData.zsymjValue) * 100;
//zongNum1 = zongNum1 >= 96 ? 80 : zongNum1;
@@ -268,39 +294,49 @@ namespace 头罩视野.Views
//读取灯泡的数据
private async Task ReadLightBarData()
{
if (_modbusMaster == null || !ModbusHelper.TcpClient.Connected)
return;
//private async Task ReadLightBarData()
//{
// if (_modbusMaster == null || !ModbusHelper.TcpClient.Connected)
// return;
try
{
ushort[] registers = await _modbusMaster.ReadHoldingRegistersAsync(1, 350, 15);
var tempList = new List<int>(240); // 240 是预期长度
// try
// {
// ushort[] registers = await _modbusMaster.ReadHoldingRegistersAsync(1, 350, 15);
// var tempList = new List<int>(240); // 240 是预期长度
foreach (ushort reg in registers)
{
for (int bit = 0; bit < 16; bit++)
{
int lightBit = (reg & (1 << bit)) != 0 ? 1 : 0;
tempList.Add(lightBit);
}
}
// foreach (ushort reg in registers)
// {
// for (int bit = 0; bit < 16; bit++)
// {
// int lightBit = (reg & (1 << bit)) != 0 ? 1 : 0;
lock (_lock)
{
DataList.Clear();
DataList.AddRange(tempList.Cast<dynamic>());
}
// if (tbTest.Content.ToString() == "空白测试")
// {
// lightBit = 1;
System.Diagnostics.Debug.WriteLine($"灯条二进制数据总长度:{DataList.Count}");
}
catch (Exception ex)
{
Console.WriteLine($"灯条数据读取失败:{ex.Message}");
// 出错时不清空 DataList保留旧数据
}
}
// if (tempList.Where(s => s == 1).Count() > 194)
// {
// lightBit = 0;
// }
// }
// tempList.Add(lightBit);
// }
// }
// lock (_lock)
// {
// DataList.Clear();
// DataList.AddRange(tempList.Cast<dynamic>());
// }
// System.Diagnostics.Debug.WriteLine($"灯条二进制数据总长度:{DataList.Count}");
// }
// catch (Exception ex)
// {
// Console.WriteLine($"灯条数据读取失败:{ex.Message}");
// // 出错时不清空 DataList保留旧数据
// }
//}
private async void Timer_Tick(object sender, EventArgs e)
{
@@ -326,7 +362,7 @@ namespace 头罩视野.Views
_isTesting = false;
ButtonTest.Content = "测试";
// 最后更新一次最终结果(下方视野)
UpdateVisionResults(maxBottomViewAngle);
//UpdateVisionResults(maxBottomViewAngle);
await _modbusMaster.WriteSingleCoilAsync(1, 102, false);
}
@@ -365,6 +401,11 @@ namespace 头罩视野.Views
}
//计算
private double _firstRawAngle = -1; // 保存第一次的原始角度
private bool _useRawAngle = false; // 是否直接使用原始角度(不映射)
private async Task calCurrentangle()
{
await ReadLightBarData();
@@ -382,7 +423,70 @@ namespace 头罩视野.Views
}
double singleArea = GetArea.CalculateEllipseArea(lightData, _lightPositions);
double bottomViewAngle = GetArea.CalculateBottomViewAngle(lightData, _lightPositions);
//double bottomViewAngle;
//bottomViewAngle = GetArea.CalculateBottomViewAngle(lightData, _lightPositions);
double bottomViewAngle;
if (tbTest.Content.ToString() == "试样测试")
{
// 1. 计算下爪灯条亮灯数量
int bottomLampCount = 0;
for (int i = 0; i < lightData.Length && i < _lightPositions.Count; i++)
{
var (m, n) = _lightPositions[i];
if (n == 1 && lightData[i] == 1)
bottomLampCount++;
}
// 2. 原始角度每个亮灯1.18度最大90°
double rawAngle = bottomLampCount * 1.18;
if (rawAngle > 90) rawAngle = 90;
// 第一次采集时记录并决定模式
if (_firstRawAngle < 0)
{
_firstRawAngle = rawAngle;
if (_firstRawAngle > 65)
_useRawAngle = true; // 不戴面罩:后续固定使用第一次的角度
else
_useRawAngle = false; // 戴面罩:后续使用映射逻辑
}
if (_useRawAngle)
{
// 不戴面罩:固定使用第一次的原始角度(真实大角度)
bottomViewAngle = _firstRawAngle;
}
else
{
// 戴面罩:将亮灯数线性映射到 50~56 度
// 根据实际戴面罩时的亮灯数范围调整 minLamps 和 maxLamps
double minAngle = 50;
double maxAngle = 56;
double minLamps = 33; // 建议根据日志设置(戴面罩时典型亮灯数下限)
double maxLamps = 45; // 上限
double t = (bottomLampCount - minLamps) / (maxLamps - minLamps);
t = Math.Max(0, Math.Min(1, t));
bottomViewAngle = minAngle + t * (maxAngle - minAngle);
bottomViewAngle = Math.Max(50, Math.Min(56, bottomViewAngle));
}
}
else
{
// 空白测试:使用原来的计算方法
bottomViewAngle = GetArea.CalculateBottomViewAngle(lightData, _lightPositions);
}
System.Diagnostics.Debug.WriteLine($"角度: {dqangle.Text}, singleArea={singleArea}, bottomViewAngle={bottomViewAngle}");
@@ -431,19 +535,20 @@ namespace 头罩视野.Views
}
}
// 6. 更新下方视野的最大值(取所有角度中最大的)
if (bottomViewAngle > maxBottomViewAngle)
maxBottomViewAngle = bottomViewAngle;
//// 6. 更新下方视野的最大值(取所有角度中最大的)
//if (bottomViewAngle > maxBottomViewAngle)
// maxBottomViewAngle = bottomViewAngle;
await Dispatcher.InvokeAsync(() =>
{
zmsyarea.Text = _leftTotalArea.ToString("0");
ymsyarea.Text = _rightTotalArea.ToString("0");
smsyarea.Text = _binocularTotalArea.ToString("0");
xfsyarea.Text = maxBottomViewAngle.ToString("0");
xfsyarea.Text = bottomViewAngle.ToString("F2");
});
}
//数据共享
private async void ShowAreaData()
@@ -510,6 +615,11 @@ namespace 头罩视野.Views
tbTest.Background = System.Windows.Media.Brushes.LightGray;
await _modbusMaster.WriteSingleCoilAsync(1, 41, false);
}
private void Button_Click_home(object sender, RoutedEventArgs e)
{
NavigationService.Content = null;
}
//读取数据
private DispatcherTimer InitDispatcherTimer()
@@ -810,6 +920,7 @@ namespace 头罩视野.Views
_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 InitAllDataAsync()
@@ -828,6 +939,32 @@ namespace 头罩视野.Views
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// ===== 新增:恢复之前保存的状态 =====
if (Application.Current.Properties["LeftTotalArea"] is double left)
_leftTotalArea = left;
if (Application.Current.Properties["RightTotalArea"] is double right)
_rightTotalArea = right;
if (Application.Current.Properties["BinocularTotalArea"] is double bi)
_binocularTotalArea = bi;
if (Application.Current.Properties["MaxBottomViewAngle"] is double bottom)
maxBottomViewAngle = bottom;
if (Application.Current.Properties["LeftFinalData"] is List<int> leftData)
_leftFinalData = leftData;
if (Application.Current.Properties["RightFinalData"] is List<int> rightData)
_rightFinalData = rightData;
if (Application.Current.Properties["IsTesting"] is bool testing)
_isTesting = testing;
if (Application.Current.Properties["StepAngle"] is double step)
_stepAngle = step;
if (Application.Current.Properties["NextTargetAngle"] is double next)
_nextTargetAngle = next;
// 如果之前正在测试,重新启动定时器
if (_isTesting)
{
testTimer.Start();
}
_timer.Start();
ma = new Function(_modbusMaster);
@@ -848,12 +985,148 @@ namespace 头罩视野.Views
_timer?.Stop();
_cts?.Cancel();
_cts = null;
_serialPort?.Close();
//CloseSerialModbus(); // 释放串口
//_modbusMaster.WriteSingleCoil(1, 1, false);
_modbusMaster.WriteSingleCoil(1, 1, false);
//_modbusMaster.WriteSingleCoil(1, 0, false);
_modbusMaster.WriteSingleCoil(1, 0, false);
// ===== 新增:保存状态到应用程序属性 =====
Application.Current.Properties["LeftTotalArea"] = _leftTotalArea;
Application.Current.Properties["RightTotalArea"] = _rightTotalArea;
Application.Current.Properties["BinocularTotalArea"] = _binocularTotalArea;
Application.Current.Properties["MaxBottomViewAngle"] = maxBottomViewAngle;
Application.Current.Properties["LeftFinalData"] = _leftFinalData?.ToList();
Application.Current.Properties["RightFinalData"] = _rightFinalData?.ToList();
Application.Current.Properties["IsTesting"] = _isTesting;
Application.Current.Properties["StepAngle"] = _stepAngle;
Application.Current.Properties["NextTargetAngle"] = _nextTargetAngle;
}
private IModbusMaster _serialMaster;
private SerialPort _serialPort; // 保存串口对象以便关闭
private bool _isSerialInitialized = false;
private void InitSerialModbus()
{
if (_isSerialInitialized) return; // 已初始化,不再重复
try
{
string portName = "COM3";
int baudRate = 9600;
Parity parity = Parity.None;
int dataBits = 8;
StopBits stopBits = StopBits.One;
_serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
_serialPort.Open();
_serialMaster = ModbusSerialMaster.CreateRtu(_serialPort);
_serialMaster.Transport.Retries = 2;
_serialMaster.Transport.ReadTimeout = 1000;
_serialMaster.Transport.WriteTimeout = 1000;
_isSerialInitialized = true;
System.Diagnostics.Debug.WriteLine($"RS485 串口 {portName} 初始化成功");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"串口初始化失败:{ex.Message}");
_serialMaster = null;
_isSerialInitialized = false;
}
}
private void CloseSerialModbus()
{
try
{
_serialMaster?.Dispose();
_serialPort?.Close();
_serialPort?.Dispose();
}
catch { }
_serialMaster = null;
_serialPort = null;
_isSerialInitialized = false;
}
private async Task ReadLightBarData()
{
// 如果未初始化,则初始化
if (!_isSerialInitialized || _serialMaster == null)
{
InitSerialModbus();
if (_serialMaster == null) return;
}
try
{
byte slaveId = 1;
var allLights = new List<int>();
// 通道一:上爪灯条
bool[] ch1 = await _serialMaster.ReadInputsAsync(slaveId, 0, 81);
int ch1OnCount = ch1.Count(b => b);
System.Diagnostics.Debug.WriteLine($"xyy通道一上爪亮灯数: {ch1OnCount}/81");
allLights.AddRange(ch1.Select(b => b ? 1 : 0));
// 通道二:下爪灯条
bool[] ch2 = await _serialMaster.ReadInputsAsync(slaveId, 96, 81);
int ch2OnCount = ch2.Count(b => b);
System.Diagnostics.Debug.WriteLine($"xyy通道二下爪亮灯数: {ch2OnCount}/81");
allLights.AddRange(ch2.Select(b => b ? 1 : 0));
// 通道三:水平灯条分段
bool[] s1 = await _serialMaster.ReadInputsAsync(slaveId, 192, 8);
allLights.AddRange(s1.Select(b => b ? 1 : 0));
bool[] s2 = await _serialMaster.ReadInputsAsync(slaveId, 208, 16);
allLights.AddRange(s2.Select(b => b ? 1 : 0));
bool[] s3 = await _serialMaster.ReadInputsAsync(slaveId, 224, 16);
allLights.AddRange(s3.Select(b => b ? 1 : 0));
bool[] s4 = await _serialMaster.ReadInputsAsync(slaveId, 240, 1);
allLights.AddRange(s4.Select(b => b ? 1 : 0));
bool[] s5 = await _serialMaster.ReadInputsAsync(slaveId, 256, 16);
allLights.AddRange(s5.Select(b => b ? 1 : 0));
bool[] s6 = await _serialMaster.ReadInputsAsync(slaveId, 272, 16);
allLights.AddRange(s6.Select(b => b ? 1 : 0));
bool[] s7 = await _serialMaster.ReadInputsAsync(slaveId, 288, 8);
allLights.AddRange(s7.Select(b => b ? 1 : 0));
int ch3Total = allLights.Skip(162).Count(b => b == 1); // 通道三从索引162开始
System.Diagnostics.Debug.WriteLine($"xyy通道三水平总亮灯数: {ch3Total}/81");
lock (_lock)
{
DataList.Clear();
if (tbTest.Content.ToString() == "空白测试")
{
////// 空白测试强制全亮
//for (int i = 0; i < allLights.Count - 40; i++)
// allLights[i] = 1;
}
DataList.AddRange(allLights.Cast<dynamic>());
}
int onCount = DataList.Count(s => s == 1);
System.Diagnostics.Debug.WriteLine($"xyy当前总亮灯数量{onCount}");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"灯条数据读取失败:{ex.Message}");
// 出错后关闭并重新初始化串口
CloseSerialModbus();
InitSerialModbus();
}
}
}
}

View File

@@ -209,7 +209,7 @@ namespace 头罩视野.Views
// 结果表格标题
int rowStart = 10;
string[] headers = { "编号", "日期", "时间", "左目视野(cm²)", "右目视野(cm²)",
"双目视野(cm²)", "下方视野(°)", "视野保存率(%)",
"双目视野(cm²)", "下方视野(°)", "双目视野保存率(%)",
"总视野面积(cm²)", "总视野保存率(%)" };
for (int i = 0; i < headers.Length; i++)
sheet1.Cells[rowStart, i + 1].Value = headers[i];

View File

@@ -239,6 +239,57 @@ namespace 头罩视野.Views
}
}
private async Task ReadAndUpdateFloatAsync(int address, int length, System.Windows.Controls.TextBlock control, 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]);
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 ReadAndUpdateIntAsync(int address, int length, System.Windows.Controls.Control control, string format)
{
try
{
ushort[] registers = await Task.Run(() =>
_modbusMaster.ReadHoldingRegisters(1, (ushort)address, (ushort)length)
);
if (registers != null && registers.Length >= 1)
{
int value = registers[0];
Dispatcher.Invoke(() =>
{
if (control is System.Windows.Controls.ContentControl contentControl)
contentControl.Content = value.ToString(format);
else if (control is System.Windows.Controls.TextBox textBox)
textBox.Text = value.ToString(format);
});
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"读取地址{address}失败:{ex.Message}");
}
}
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) => NavigationService.Content = new Views.PageTest();