999 lines
37 KiB
C#
999 lines
37 KiB
C#
using Microsoft.Win32;
|
||
using Modbus.Device;
|
||
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;
|
||
using System.Text;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Controls.Primitives;
|
||
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 System.Windows.Threading;
|
||
using 头罩视野.Services;
|
||
using 头罩视野.Services.Data;
|
||
using static 头罩视野.TestDataStore;
|
||
namespace 头罩视野.Views
|
||
{
|
||
public partial class PageTest : Page
|
||
{
|
||
/// 只加这一个变量
|
||
private CancellationTokenSource? _cts;
|
||
private IModbusMaster _modbusMaster => ModbusResourceManager.Instance.ModbusMaster;
|
||
DispatcherTimer _timer;
|
||
DataChange c = new DataChange();
|
||
Function ma;
|
||
bool isFinished;
|
||
//// 定时采集用
|
||
private DispatcherTimer testTimer;
|
||
|
||
//private DispatcherTimer testTimerForLight;
|
||
// 保存上一条数据(用于去重)
|
||
private TestDataStore.TestRecord? _lastRecord;
|
||
|
||
|
||
#region 当前角度
|
||
private double currentAngle;
|
||
#endregion
|
||
|
||
|
||
// 极坐标积分数据
|
||
private List<double> _leftBoundaries = new List<double>(); // 左眼各角度边界半径
|
||
private List<double> _rightBoundaries = new List<double>(); // 右眼各角度边界半径
|
||
private List<double> _leftMeasuredAngles = new List<double>();
|
||
private List<double> _rightMeasuredAngles = new List<double>();
|
||
private double _lastBoundaryLeft = 0;
|
||
private double _lastBoundaryRight = 0;
|
||
private double _lastAngle = -1;
|
||
|
||
|
||
|
||
|
||
private double _binocularTotalArea = 0; // 双目总视野面积
|
||
double maxBottomViewAngle = 0; //记录所有姿态里的最大下方视野
|
||
|
||
// 最终左眼视野(永远243个)
|
||
private List<int> _leftFinalData = new List<int>();
|
||
// 最终右眼视野(永远243个)
|
||
private List<int> _rightFinalData = new List<int>();
|
||
|
||
// 表跟数据存储列表
|
||
public List<dynamic> DataList = new List<dynamic>();
|
||
|
||
public PageTest()
|
||
{
|
||
InitializeComponent();
|
||
InitLightPositions();
|
||
InitAllDataAsync();
|
||
_timer = InitDispatcherTimer();
|
||
// 2. 初始化定时器:500毫秒 执行一次
|
||
|
||
testTimer = new DispatcherTimer();
|
||
testTimer.Interval = TimeSpan.FromMilliseconds(500); // 500ms = 0.5秒
|
||
testTimer.Tick += Timer_Tick;
|
||
|
||
|
||
//testTimerForLight = new DispatcherTimer();
|
||
//testTimerForLight.Interval = TimeSpan.FromMilliseconds(1000); // 500ms = 0.5秒
|
||
//testTimerForLight.Tick += testTimerForLightTick;
|
||
|
||
//// 判断连接
|
||
if (!ModbusHelper.IsConnected)
|
||
{
|
||
MessageBox.Show("未连接");
|
||
return;
|
||
}
|
||
//if (GlobalData.zsymjValue > 0 && !double.IsNaN(GlobalData.zsymjValue) && !double.IsInfinity(GlobalData.zsymjValue))
|
||
//{
|
||
// // 直接调用测试方法,简单能用
|
||
// TbTest_Checked(null, null);
|
||
//}
|
||
}
|
||
|
||
// 硬件固定参数(提前定义好,不要改)
|
||
private const int LightsPerStrip = 81; // 单条灯条81个灯
|
||
private const int HalfLights = (LightsPerStrip - 1) / 2; // 40,给左右灯条用
|
||
double stepAngle;
|
||
|
||
private bool _isTesting = false; // 是否正在测试
|
||
private double _stepAngle = 10.0; // 分辨角度
|
||
private double _nextTargetAngle = 0; // 下一次要采集的角度(0, stepAngle, 2*stepAngle, ...)
|
||
|
||
bool isLeftOnly = false;
|
||
bool isRightOnly = false;
|
||
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));
|
||
}
|
||
for (int m = 0; m < LightsPerStrip; 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}");
|
||
}
|
||
|
||
//停止btn
|
||
private void Button_Click_Stop(object sender, RoutedEventArgs e)
|
||
{
|
||
ma.BtnClickFunction(Function.ButtonType.复归型, 103);
|
||
ButtonTest.Content = "测试";
|
||
_isTesting = false;
|
||
testTimer.Stop();
|
||
}
|
||
|
||
//测试按钮
|
||
private async void Button_Click_Test(object sender, RoutedEventArgs e)
|
||
{
|
||
if (_isTesting)
|
||
{
|
||
// 停止测试
|
||
await _modbusMaster.WriteSingleCoilAsync(1, 11, false);
|
||
_isTesting = false;
|
||
ButtonTest.Content = "测试";
|
||
testTimer.Stop();
|
||
return;
|
||
}
|
||
|
||
// 1. 获取分辨角度
|
||
if (!double.TryParse(fbspeed.Text.Trim(), out double step))
|
||
{
|
||
MessageBox.Show("请输入有效的分辨角度(5~30)");
|
||
return;
|
||
}
|
||
_stepAngle = Math.Max(1, Math.Min(30, step));
|
||
_nextTargetAngle = _stepAngle;
|
||
_isTesting = true;
|
||
ButtonTest.Content = "测试中...";
|
||
|
||
|
||
// 在开始测试前清空当前眼的数据,保留另一眼的数据(如果有)
|
||
if (btnLeft.Content.ToString() == "左眼关" && btnRight.Content.ToString() == "右眼开")
|
||
{
|
||
_leftBoundaries.Clear();
|
||
_leftMeasuredAngles.Clear(); // 改为清空左眼角度
|
||
_lastAngle = -1;
|
||
_lastBoundaryLeft = 0;
|
||
}
|
||
else if (btnLeft.Content.ToString() == "左眼开" && btnRight.Content.ToString() == "右眼关")
|
||
{
|
||
_rightBoundaries.Clear();
|
||
_rightMeasuredAngles.Clear(); // 改为清空右眼角度
|
||
_lastAngle = -1;
|
||
_lastBoundaryRight = 0;
|
||
}
|
||
maxBottomViewAngle = 0;
|
||
// 面积也清空
|
||
|
||
|
||
isLeftOnly = btnLeft.Content.ToString() == "左眼关" && btnRight.Content.ToString() == "右眼开";
|
||
isRightOnly = btnLeft.Content.ToString() == "左眼开" && btnRight.Content.ToString() == "右眼关";
|
||
isBinocular = btnLeft.Content.ToString() == "左眼关" && btnRight.Content.ToString() == "右眼关";
|
||
|
||
await calCurrentangle();
|
||
|
||
_nextTargetAngle = _stepAngle;
|
||
|
||
ma.BtnClickFunction(Function.ButtonType.复归型, 100);
|
||
|
||
// 6. 启动定时器(每500ms检查一次角度)
|
||
testTimer.Start();
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
private async void Timer_Tick(object sender, EventArgs e)
|
||
{
|
||
if (!_isTesting) return; // 非测试状态直接返回
|
||
|
||
if (!double.TryParse(dqangle.Text.Replace("°", ""), out double currentAngle))
|
||
return;
|
||
|
||
// 判断是否达到下一个目标角度(允许±1°误差)
|
||
if (currentAngle >= _nextTargetAngle - 1.0)
|
||
{
|
||
// 采集当前角度的数据
|
||
await calCurrentangle();
|
||
|
||
// 更新下一个目标角度
|
||
_nextTargetAngle += _stepAngle;
|
||
|
||
// 如果超过180°,结束测试
|
||
if (_nextTargetAngle >= 180.0 - 1e-6)
|
||
{
|
||
await _modbusMaster.WriteSingleCoilAsync(1, 11, false);
|
||
_isTesting = false;
|
||
ButtonTest.Content = "测试";
|
||
testTimer.Stop();
|
||
await _modbusMaster.WriteSingleCoilAsync(1, 102, false);
|
||
FinishTestAndCompute(); // 新方法
|
||
}
|
||
}
|
||
}
|
||
|
||
private void FinishTestAndCompute()
|
||
{
|
||
if (_leftBoundaries.Count == 0 || _rightBoundaries.Count == 0)
|
||
{
|
||
// 只测了一只眼,提示用户完成另一只眼
|
||
MessageBox.Show("请完成另一只眼睛的测试");
|
||
return;
|
||
}
|
||
|
||
// 将左右眼边界数组插值到标准角度网格(步长 _stepAngle)
|
||
int targetCount = (int)(180 / _stepAngle) + 1;
|
||
double[] targetAngles = new double[targetCount];
|
||
for (int i = 0; i < targetCount; i++) targetAngles[i] = i * _stepAngle;
|
||
|
||
double[] leftInterp = InterpolateAngles(_leftMeasuredAngles.ToArray(), _leftBoundaries.ToArray(), targetAngles);
|
||
double[] rightInterp = InterpolateAngles(_rightMeasuredAngles.ToArray(), _rightBoundaries.ToArray(), targetAngles);
|
||
|
||
// 计算单眼面积(积分)
|
||
double leftArea = GetArea.IntegrateArea(leftInterp, _stepAngle);
|
||
double rightArea = GetArea.IntegrateArea(rightInterp, _stepAngle);
|
||
rightArea *= 1.7;
|
||
var (totalArea, biArea) = GetArea.ComputeTotalAndBinocularArea(leftInterp, rightInterp, _stepAngle);
|
||
System.Diagnostics.Debug.WriteLine($"左眼边界数组: {string.Join(",", _leftBoundaries)}");
|
||
System.Diagnostics.Debug.WriteLine($"左眼角度数组: {string.Join(",", _leftMeasuredAngles)}");
|
||
System.Diagnostics.Debug.WriteLine($"右眼边界数组: {string.Join(",", _rightBoundaries)}");
|
||
System.Diagnostics.Debug.WriteLine($"右眼角度数组: {string.Join(",", _rightMeasuredAngles)}");
|
||
|
||
System.Diagnostics.Debug.WriteLine($"左眼面积: {leftArea}");
|
||
// 等等
|
||
// 更新界面
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
zmsyarea.Text = leftArea.ToString("F2");
|
||
ymsyarea.Text = rightArea.ToString("F2");
|
||
smsyarea.Text = biArea.ToString("F2");
|
||
zsyareaNum.Text = totalArea.ToString("F2");
|
||
xfsyarea.Text = maxBottomViewAngle.ToString("F0");
|
||
|
||
if (tbTest.Content.ToString() == "空白测试")
|
||
{
|
||
GlobalData.zsymjValue = totalArea;
|
||
GlobalData.kbsmsyArea = biArea;
|
||
sybhl.Text = "100.0";
|
||
zsysaveSum.Text = "100.0";
|
||
}
|
||
else if (tbTest.Content.ToString() == "试样测试")
|
||
{
|
||
double totalRate = GetArea.ComputePreservation(totalArea, GlobalData.zsymjValue,
|
||
GetArea.GetGammaByRatio(totalArea / GlobalData.zsymjValue));
|
||
double biRate = GetArea.ComputePreservation(biArea, GlobalData.kbsmsyArea,
|
||
GetArea.GetGammaByRatio(biArea / GlobalData.kbsmsyArea));
|
||
sybhl.Text = biRate.ToString("F2");
|
||
zsysaveSum.Text = totalRate.ToString("F2");
|
||
}
|
||
});
|
||
|
||
ShowAreaData(); // 保存记录
|
||
}
|
||
|
||
// 辅助插值函数
|
||
private double[] InterpolateAngles(double[] origAngles, double[] origRadii, double[] targetAngles)
|
||
{
|
||
double[] result = new double[targetAngles.Length];
|
||
for (int i = 0; i < targetAngles.Length; i++)
|
||
{
|
||
double target = targetAngles[i];
|
||
if (target <= origAngles[0]) result[i] = origRadii[0];
|
||
else if (target >= origAngles[origAngles.Length - 1]) result[i] = origRadii[origRadii.Length - 1];
|
||
else
|
||
{
|
||
for (int j = 0; j < origAngles.Length - 1; j++)
|
||
{
|
||
if (target >= origAngles[j] && target <= origAngles[j + 1])
|
||
{
|
||
double t = (target - origAngles[j]) / (origAngles[j + 1] - origAngles[j]);
|
||
result[i] = origRadii[j] + t * (origRadii[j + 1] - origRadii[j]);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
|
||
private async void testTimerForLightTick(object sender, EventArgs e)
|
||
{
|
||
|
||
//if (btnLeft.Content.ToString() == "左眼开" && btnRight.Content.ToString() == "右眼开")
|
||
// return;
|
||
if (_modbusMaster == null)
|
||
return;
|
||
|
||
var ret = await _modbusMaster.ReadCoilsAsync(1, 0, 2);
|
||
|
||
if (ret != null && ret.Length > 0)
|
||
{ //左眼开
|
||
if (ret[0])
|
||
{
|
||
LedOff(led1);
|
||
LedOn(led0);
|
||
|
||
btnLeft.Content = "左眼关";
|
||
btnRight.Content = "右眼开";
|
||
}
|
||
else if (ret[1])
|
||
{
|
||
|
||
LedOff(led0);
|
||
LedOn(led1);
|
||
btnLeft.Content = "左眼开";
|
||
btnRight.Content = "右眼关";
|
||
|
||
|
||
}
|
||
}
|
||
|
||
}
|
||
//计算
|
||
|
||
|
||
private async Task calCurrentangle()
|
||
{
|
||
await ReadLightBarData();
|
||
|
||
int[] lightData;
|
||
lock (_lock)
|
||
{
|
||
lightData = DataList.Cast<int>().ToArray();
|
||
}
|
||
if (lightData.Length == 0) return;
|
||
|
||
if (!double.TryParse(dqangle.Text.Replace("°", ""), out double currentAngleDeg)) return;
|
||
|
||
// 统计下爪灯条(n==1)亮灯数量
|
||
int bottomLampCount = 0;
|
||
for (int i = 0; i < lightData.Length && i < _lightPositions.Count; i++)
|
||
{
|
||
if (_lightPositions[i].n == 1 && lightData[i] == 1)
|
||
bottomLampCount++;
|
||
}
|
||
|
||
// 边界角度(用于面积积分)和下方视野都使用亮灯比例 * 90°
|
||
double boundaryDeg = (double)bottomLampCount / 81 * 90.0;
|
||
double bottom = boundaryDeg; // 下方视野与边界角度一致,但最大值取所有角度中的最大
|
||
|
||
System.Diagnostics.Debug.WriteLine($"当前模式: isLeftOnly={isLeftOnly}, isRightOnly={isRightOnly}");
|
||
System.Diagnostics.Debug.WriteLine($"下爪亮灯数: {bottomLampCount}/81, 边界角度: {boundaryDeg:F2}°");
|
||
|
||
// 记录边界角度(用于后期极坐标积分)
|
||
if (isLeftOnly)
|
||
{
|
||
_leftBoundaries.Add(boundaryDeg);
|
||
_leftMeasuredAngles.Add(currentAngleDeg);
|
||
}
|
||
else if (isRightOnly)
|
||
{
|
||
_rightBoundaries.Add(boundaryDeg);
|
||
_rightMeasuredAngles.Add(currentAngleDeg);
|
||
}
|
||
|
||
// 更新下方视野(取所有角度中的最大值)
|
||
if (bottom > maxBottomViewAngle)
|
||
maxBottomViewAngle = bottom;
|
||
|
||
await Dispatcher.InvokeAsync(() =>
|
||
{
|
||
xfsyarea.Text = maxBottomViewAngle.ToString("F0");
|
||
});
|
||
}
|
||
|
||
//数据共享
|
||
private async void ShowAreaData()
|
||
|
||
{
|
||
// 组装当前数据
|
||
var data = new TestDataStore.TestRecord
|
||
{
|
||
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
||
Time = DateTime.Now.ToString("HH:mm:ss"),
|
||
LeftEyeArea = double.TryParse(zmsyarea.Text, out var l) ? l : 0,
|
||
RightEyeArea = double.TryParse(ymsyarea.Text, out var r) ? r : 0,
|
||
BinocularArea = double.TryParse(smsyarea.Text, out var b) ? b : 0,
|
||
LowerVision = double.TryParse(xfsyarea.Text, out var lv) ? lv : 0,
|
||
VisionRetentionRate = double.TryParse(sybhl.Text, out var vr) ? vr : 0,
|
||
totalVisionArea = double.TryParse(zsyareaNum.Text, out var vrt) ? vrt : 0,
|
||
GetVisionRetentionRate = double.TryParse(zsysaveSum.Text, out var vrc) ? vrc : 0,
|
||
|
||
};
|
||
|
||
// ==================== 去重判断 ====================
|
||
if (_lastRecord != null &&
|
||
data.LeftEyeArea == _lastRecord.LeftEyeArea &&
|
||
data.RightEyeArea == _lastRecord.RightEyeArea &&
|
||
data.BinocularArea == _lastRecord.BinocularArea &&
|
||
data.LowerVision == _lastRecord.LowerVision &&
|
||
data.VisionRetentionRate == _lastRecord.VisionRetentionRate &&
|
||
data.totalVisionArea == _lastRecord.totalVisionArea &&
|
||
data.GetVisionRetentionRate == _lastRecord.GetVisionRetentionRate
|
||
)
|
||
{
|
||
return; // 一样就不添加
|
||
}
|
||
if (data.BinocularArea == 0)
|
||
{
|
||
return;
|
||
}
|
||
//原来存的数据清空 切换页面会清空
|
||
//TestDataStore.Records.Clear();
|
||
// 不一样 → 插入表格
|
||
TestDataStore.AddNewRecord(data);
|
||
_lastRecord = data;
|
||
}
|
||
//试样测试
|
||
|
||
|
||
private async void TbTest_Unchecked(object sender, RoutedEventArgs e)
|
||
{
|
||
// 选中 → 试样测试
|
||
|
||
|
||
tbTest.Content = "空白测试";
|
||
GlobalData.CurrentMode = "空白测试";
|
||
|
||
tbTest.Background = System.Windows.Media.Brushes.LightSkyBlue;
|
||
await _modbusMaster.WriteSingleCoilAsync(1, 41, true);
|
||
}
|
||
|
||
private async void TbTest_Checked(object sender, RoutedEventArgs e)
|
||
{
|
||
// 取消 → 空白测试
|
||
|
||
tbTest.Content = "试样测试";
|
||
GlobalData.CurrentMode = "试样测试";
|
||
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()
|
||
{
|
||
var timer = new DispatcherTimer
|
||
{
|
||
Interval = TimeSpan.FromMilliseconds(500)
|
||
};
|
||
timer.Tick += async (s, e) =>
|
||
{
|
||
if (_modbusMaster != null)
|
||
{
|
||
try
|
||
{
|
||
await ReadAddr262DataAsync();
|
||
}
|
||
catch { }
|
||
}
|
||
};
|
||
return timer;
|
||
}
|
||
|
||
private async System.Threading.Tasks.Task ReadAddr262DataAsync()
|
||
{
|
||
try
|
||
{
|
||
// 创建任务列表
|
||
var tasks = new List<Task>
|
||
{
|
||
|
||
ReadAndUpdateFloatRangeAsync(200, 12, "F2", "°"),
|
||
|
||
ReadAndUpdateFloatAsync(310, 2, zdangle, "F2", ""),
|
||
};
|
||
isFinished = _modbusMaster.ReadCoils(1, 102, 1)[0];
|
||
if (isFinished)
|
||
{
|
||
Button_Click_Stop(null, null);
|
||
|
||
await _modbusMaster.WriteSingleCoilAsync(1, 102, false);
|
||
isFinished = false;
|
||
|
||
}
|
||
|
||
|
||
if (double.TryParse(fbspeed.Text, out double result))
|
||
{
|
||
GlobalData.JudgmentalPerspective = result.ToString();
|
||
}
|
||
|
||
await Task.WhenAll(tasks);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError($"读取数据失败:{ex.Message}");
|
||
}
|
||
}
|
||
// 地址, 根据格式显示字符长度 32:2 16:1 ,绑定页面的name 值,F2 保留两位小数,单位
|
||
private async Task ReadAndUpdateFloatAsync(int address, int length, System.Windows.Controls.TextBox control, string format, string unit)
|
||
{
|
||
try
|
||
{ // 1. 页面销毁时直接退出,不执行
|
||
if (_cts?.IsCancellationRequested == true) return;
|
||
|
||
// 2. 关键:判断对象为空就直接返回,不执行
|
||
if (_modbusMaster == null) return;
|
||
|
||
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 ReadAndUpdateFloatRangeAsync(int address, int length, 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]);
|
||
float value2 = c.UshortToFloat(registers[3], registers[2]);
|
||
float value3 = c.UshortToFloat(registers[5], registers[4]);
|
||
//float value4 = c.UshortToFloat(registers[7], registers[6]);
|
||
//float value5 = c.UshortToFloat(registers[9], registers[8]);
|
||
//float value6 = c.UshortToFloat(registers[11], registers[10]);
|
||
currentAngle = value2;
|
||
|
||
Dispatcher.Invoke(() =>
|
||
|
||
{
|
||
fbspeed.Text = value.ToString(format);
|
||
dqangle.Text = value2.ToString(format);
|
||
//zmsyarea.Text = value3.ToString(format) + unit;
|
||
//xfsyarea.Text = value4.ToString(format) + unit;
|
||
//smsyarea.Text = value5.ToString(format) + unit;
|
||
//ymsyarea.Text = value6.ToString(format) + unit;
|
||
//control.Text = value.ToString(format) + unit);
|
||
});
|
||
}
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"读取地址{address}失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
//打印
|
||
|
||
private void Button_Click_Print(object sender, RoutedEventArgs e)
|
||
{
|
||
|
||
ma.BtnClickFunction(Function.ButtonType.复归型, 103);
|
||
|
||
}
|
||
|
||
|
||
// 蓝色亮(蓝色)
|
||
private void LedOn(Ellipse led)
|
||
{
|
||
led.Fill = Brushes.LightSkyBlue;
|
||
}
|
||
|
||
// 灯灭(灰色)
|
||
private void LedOff(Ellipse led)
|
||
{
|
||
led.Fill = Brushes.LightGray;
|
||
}
|
||
|
||
//复位btn
|
||
private void Button_Click_Reset(object sender, RoutedEventArgs e)
|
||
{
|
||
//btnLeft.Content = "左眼开";
|
||
//btnRight.Content = "右眼开";
|
||
//LedOff(led1);
|
||
//LedOff(led0);
|
||
|
||
|
||
|
||
|
||
_binocularTotalArea = 0;
|
||
maxBottomViewAngle = 0;
|
||
|
||
//初始化:全部设为0(灭)
|
||
_leftFinalData = Enumerable.Repeat(0, 240).ToList();
|
||
_rightFinalData = Enumerable.Repeat(0, 240).ToList();
|
||
DataList.Clear();
|
||
ma.BtnClickFunction(Function.ButtonType.复归型, 90);
|
||
zmsyarea.Text = "0"; // 左目
|
||
smsyarea.Text = "0"; // 双目
|
||
kbsyarea.Text = "0"; // 空白
|
||
ymsyarea.Text = "0"; // 右目
|
||
xfsyarea.Text = "0"; // 下方
|
||
sybhl.Text = "0"; // 视野保存率
|
||
zsyareaNum.Text = "0";
|
||
zsysaveSum.Text = "0";
|
||
}
|
||
//左开眼
|
||
private void Button_Click_left(object sender, RoutedEventArgs e)
|
||
{
|
||
// 切换文案:开是关,关是开
|
||
if (btnLeft.Content.ToString() == "左眼开")
|
||
{
|
||
|
||
|
||
btnLeft.Content = "左眼关";
|
||
LedOn(led0);
|
||
|
||
if (btnRight.Content.ToString() == "右眼关")
|
||
{
|
||
btnRight.Content = "右眼开";
|
||
LedOff(led1);
|
||
ma.BtnClickFunction(Function.ButtonType.切换型, 1);
|
||
|
||
}
|
||
}
|
||
else
|
||
{
|
||
btnLeft.Content = "左眼开";
|
||
LedOff(led0);
|
||
}
|
||
ma.BtnClickFunction(Function.ButtonType.切换型, 0);
|
||
//GlobalData.LampValueLeft = btnLeft.Content.ToString();
|
||
}
|
||
//右开眼
|
||
private void Button_Click_Right(object sender, RoutedEventArgs e)
|
||
{
|
||
|
||
// 切换文案:左眼开 ↔ 左眼关
|
||
if (btnRight.Content.ToString() == "右眼开")
|
||
{
|
||
btnRight.Content = "右眼关";
|
||
LedOn(led1);
|
||
if (btnLeft.Content.ToString() == "左眼关")
|
||
{
|
||
|
||
btnLeft.Content = "左眼开";
|
||
LedOff(led0);
|
||
ma.BtnClickFunction(Function.ButtonType.切换型, 0);
|
||
|
||
}
|
||
}
|
||
else
|
||
{
|
||
btnRight.Content = "右眼开";
|
||
LedOff(led1);
|
||
|
||
}
|
||
ma.BtnClickFunction(Function.ButtonType.切换型, 1);
|
||
//GlobalData.LampValueLeft = btnLeft.Content.ToString();
|
||
}
|
||
|
||
//反转
|
||
private async void Button_Click_ResDown(object sender, MouseButtonEventArgs e)
|
||
{
|
||
await _modbusMaster.WriteSingleCoilAsync(1, 10, true);
|
||
System.Diagnostics.Debug.WriteLine("反转开始");
|
||
}
|
||
|
||
private async void Button_Click_ResUp(object sender, MouseButtonEventArgs e)
|
||
{
|
||
await _modbusMaster.WriteSingleCoilAsync(1, 10, false);
|
||
System.Diagnostics.Debug.WriteLine("反转结束");
|
||
|
||
}
|
||
//正转
|
||
|
||
private async void Button_Click_ForDown(object sender, MouseButtonEventArgs e)
|
||
{
|
||
await _modbusMaster.WriteSingleCoilAsync(1, 11, true);
|
||
}
|
||
|
||
private async void Button_Click_ForUp(object sender, MouseButtonEventArgs e)
|
||
{
|
||
|
||
await _modbusMaster.WriteSingleCoilAsync(1, 11, false);
|
||
//System.Diagnostics.Debug.WriteLine("正传end");
|
||
}
|
||
|
||
//写入
|
||
private void fbspeed_GotFocus(object sender, RoutedEventArgs e)
|
||
{
|
||
ma.WriteToPLCForNew(fbspeed.Text.Trim(), 200, Function.DataType.浮点型);
|
||
//程序等待50s 在进行
|
||
System.Threading.Tasks.Task.Delay(50);
|
||
fbspeed.Focus();
|
||
}
|
||
private void dqangle_GotFocus(object sender, RoutedEventArgs e)
|
||
{
|
||
ma.WriteToPLCForNew(dqangle.Text.Trim(), 202, Function.DataType.浮点型);
|
||
System.Threading.Tasks.Task.Delay(50);
|
||
dqangle.Focus();
|
||
}
|
||
private void zdangle_GotFocus(object sender, RoutedEventArgs e)
|
||
{
|
||
ma.WriteToPLCForNew(zdangle.Text.Trim(), 310, Function.DataType.浮点型);
|
||
System.Threading.Tasks.Task.Delay(50);
|
||
zdangle.Focus();
|
||
}
|
||
|
||
//错误信息提示
|
||
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)
|
||
{
|
||
_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()
|
||
{
|
||
|
||
//System.Diagnostics.Debug.WriteLine($"数据加载了啊 ");
|
||
// 从全局数据读取当前模式
|
||
string mode = GlobalData.CurrentMode;
|
||
|
||
if (mode == "试样测试")
|
||
{
|
||
TbTest_Checked(null, null);
|
||
}
|
||
testTimerForLightTick(null, null);
|
||
}
|
||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||
{
|
||
// ===== 新增:恢复之前保存的状态 =====
|
||
if (Application.Current.Properties["LeftBoundaries"] is List<double> leftB)
|
||
_leftBoundaries = leftB;
|
||
if (Application.Current.Properties["RightBoundaries"] is List<double> rightB)
|
||
_rightBoundaries = rightB;
|
||
// 恢复左右眼独立的角度列表
|
||
if (Application.Current.Properties["LeftMeasuredAngles"] is List<double> leftAngles)
|
||
_leftMeasuredAngles = leftAngles;
|
||
if (Application.Current.Properties["RightMeasuredAngles"] is List<double> rightAngles)
|
||
_rightMeasuredAngles = rightAngles;
|
||
|
||
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);
|
||
c = new DataChange();
|
||
}
|
||
|
||
private void Page_Unloaded(object sender, RoutedEventArgs e)
|
||
{
|
||
testTimer?.Stop();
|
||
_timer?.Stop();
|
||
_cts?.Cancel();
|
||
_cts = null;
|
||
_serialPort?.Close();
|
||
|
||
// ===== 新增:保存状态到应用程序属性 =====
|
||
Application.Current.Properties["LeftBoundaries"] = _leftBoundaries?.ToList();
|
||
Application.Current.Properties["RightBoundaries"] = _rightBoundaries?.ToList();
|
||
// 保存左右眼独立的角度列表
|
||
Application.Current.Properties["LeftMeasuredAngles"] = _leftMeasuredAngles?.ToList();
|
||
Application.Current.Properties["RightMeasuredAngles"] = _rightMeasuredAngles?.ToList();
|
||
|
||
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 = "COM5";
|
||
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, 1, 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, 97, 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, 193, 8);
|
||
allLights.AddRange(s1.Select(b => b ? 1 : 0));
|
||
bool[] s2 = await _serialMaster.ReadInputsAsync(slaveId, 209, 16);
|
||
allLights.AddRange(s2.Select(b => b ? 1 : 0));
|
||
bool[] s3 = await _serialMaster.ReadInputsAsync(slaveId, 225, 16);
|
||
allLights.AddRange(s3.Select(b => b ? 1 : 0));
|
||
bool[] s4 = await _serialMaster.ReadInputsAsync(slaveId, 241, 1);
|
||
allLights.AddRange(s4.Select(b => b ? 1 : 0));
|
||
bool[] s5 = await _serialMaster.ReadInputsAsync(slaveId, 257, 16);
|
||
allLights.AddRange(s5.Select(b => b ? 1 : 0));
|
||
bool[] s6 = await _serialMaster.ReadInputsAsync(slaveId, 273, 16);
|
||
allLights.AddRange(s6.Select(b => b ? 1 : 0));
|
||
bool[] s7 = await _serialMaster.ReadInputsAsync(slaveId, 289, 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();
|
||
}
|
||
}
|
||
|
||
|
||
|
||
}
|
||
}
|
||
|
||
|