Files
hoodFieldOfView/头罩视野slove/头罩视野/Views/RecordPage.xaml.cs
2026-05-18 15:35:11 +08:00

406 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Modbus.Device;
using OfficeOpenXml;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Net.Sockets;
using System.Text;
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;
using System.Windows.Threading;
using System.Linq;
using System.Threading;
using .Services.Data;
using static .TestDataStore;
namespace .Views
{
/// <summary>
/// RecordPage.xaml 的交互逻辑
/// </summary>
public partial class RecordPage : Page
{
//长按清除
private bool _isClearPressed = false;
private Thread _clearThread;
private IModbusMaster _modbusMaster => ModbusResourceManager.Instance.ModbusMaster;
public RecordPage()
{
InitializeComponent();
}
//清除
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()
{
TestDataStore.Records.Clear();
RecordDataGrid.ItemsSource = null;
RecordDataGrid.ItemsSource = TestDataStore.Records;
MessageBox.Show("数据已清除");
}
private void btnClear_MouseUp(object sender, MouseButtonEventArgs e)
{
_isClearPressed = false;
_clearThread?.Join(100); // 等待线程结束最多100毫秒然后强制结束
}
private void Page_Unloaded(object sender, RoutedEventArgs e)
{
//_plcReadTimer?.Stop();
//_plcReadTimer?.Dispose();
//_modbusMaster?.Dispose();
//ModbusHelper.TcpClient?.Close();
}
////#endregion
////#region 3. 保存为Excel
//private void btnSave_Click(object sender, RoutedEventArgs e)
//{
// try
// {
// // 1. 构建表格内容
// StringBuilder sb = new StringBuilder();
// sb.AppendLine("编号,日期,时间,左目视野面积,右目视野面积,双目视野面积,空白视野面积,下方视野,视野保存率");
// foreach (var item in TestDataStore.Records)
// {
// sb.AppendLine($"{item.Id},{item.Date},{item.Time},{item.LeftEyeArea},{item.RightEyeArea},{item.BinocularArea},{item.LowerVision},{item.VisionRetentionRate}");
// }
// // 2. 弹出保存框,让用户自己选位置
// Microsoft.Win32.SaveFileDialog saveFileDialog = new Microsoft.Win32.SaveFileDialog();
// saveFileDialog.Filter = "CSV 文件 (*.csv)|*.csv|所有文件 (*.*)|*.*";
// saveFileDialog.FileName = $"测试记录_{DateTime.Now:yyyyMMddHHmmss}";
// if (saveFileDialog.ShowDialog() == true)
// {
// // 3. 保存文件
// File.WriteAllText(saveFileDialog.FileName, sb.ToString(), Encoding.UTF8);
// MessageBox.Show("保存成功!\n文件路径" + saveFileDialog.FileName, "成功");
// }
// }
// catch (Exception ex)
// {
// MessageBox.Show("保存失败:" + ex.Message);
// }
//}
//#endregion
private List<TestDataStore.TestRecord> GenerateMockRecords()
{
var mockList = new List<TestDataStore.TestRecord>();
Random rand = new Random();
for (int i = 1; i <= 5; i++)
{
double left = rand.Next(6000, 8500);
double right = rand.Next(6000, 8500);
double binocular = (left + right) / 2 * 0.85;
double lower = rand.Next(30, 50);
double totalArea = left + right - binocular; // 总视野面积
double preservation = totalArea / 12000 * 100; // 视野保存率(%
double totalPreserve = preservation * 0.95; // 总视野保存率(示例)
mockList.Add(new TestDataStore.TestRecord
{
Id = i,
Date = DateTime.Now.AddDays(-i).ToString("yyyy-MM-dd"),
Time = DateTime.Now.AddHours(-i).ToString("HH:mm:ss"),
LeftEyeArea = left,
RightEyeArea = right,
BinocularArea = binocular,
LowerVision = lower,
VisionRetentionRate = preservation,
totalVisionArea = totalArea, // ✅ 必须赋值
GetVisionRetentionRate = totalPreserve // ✅ 必须赋值
});
}
return mockList;
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
//// 判断是否有真实数据
//bool hasRealData = TestDataStore.Records != null && TestDataStore.Records.Count > 0;
//List<TestDataStore.TestRecord> exportRecords;
//if (!hasRealData)
//{
// exportRecords = GenerateMockRecords();
// TestDataStore.Records.AddRange(exportRecords);
//}
//else
//{
// exportRecords = TestDataStore.Records.ToList();
//}
try
{
ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial;
var saveDialog = new Microsoft.Win32.SaveFileDialog
{
Filter = "Excel 文件 (*.xlsx)|*.xlsx",
FileName = $"视野测试报告_{DateTime.Now:yyyyMMddHHmmss}.xlsx"
};
if (saveDialog.ShowDialog() != true) return;
using (var package = new OfficeOpenXml.ExcelPackage())
{
// ========== 工作表1报告摘要 ==========
var sheet1 = package.Workbook.Worksheets.Add("报告摘要");
// 标题
sheet1.Cells["A1"].Value = "头罩视野测试报告";
sheet1.Cells["A1"].Style.Font.Size = 22;
sheet1.Cells["A1"].Style.Font.Bold = true;
sheet1.Cells["A1"].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
sheet1.Cells["A1:G1"].Merge = true;
// 报告编号和日期
sheet1.Cells["A3"].Value = "报告编号:";
sheet1.Cells["B3"].Value = $"REP-{DateTime.Now:yyyyMMddHHmmss}";
sheet1.Cells["D3"].Value = "试验日期:";
sheet1.Cells["E3"].Value = DateTime.Now.ToString("yyyy年MM月dd日");
sheet1.Cells["A3:E3"].Style.Font.Size = 12;
// 测试参数
sheet1.Cells["A5"].Value = "测试参数";
sheet1.Cells["A5"].Style.Font.Bold = true;
sheet1.Cells["A6"].Value = "设备型号";
sheet1.Cells["B6"].Value = "X015";
sheet1.Cells["A7"].Value = "分辨角度";
sheet1.Cells["B7"].Value = GlobalData.JudgmentalPerspective;
sheet1.Cells["A8"].Value = "当前模式";
sheet1.Cells["B8"].Value = GlobalData.CurrentMode;
// 结果表格标题
int rowStart = 10;
string[] headers = { "编号", "日期", "时间", "左目视野(cm²)", "右目视野(cm²)",
"双目视野(cm²)", "下方视野(°)", "双目视野保存率(%)",
"总视野面积(cm²)", "总视野保存率(%)" };
for (int i = 0; i < headers.Length; i++)
sheet1.Cells[rowStart, i + 1].Value = headers[i];
sheet1.Cells[rowStart, 1, rowStart, headers.Length].Style.Font.Bold = true;
sheet1.Cells[rowStart, 1, rowStart, headers.Length].Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
sheet1.Cells[rowStart, 1, rowStart, headers.Length].Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightGray);
// 填充数据
int row = rowStart + 1;
foreach (var item in TestDataStore.Records)
{
sheet1.Cells[row, 1].Value = item.Id;
sheet1.Cells[row, 2].Value = item.Date;
sheet1.Cells[row, 3].Value = item.Time;
sheet1.Cells[row, 4].Value = item.LeftEyeArea;
sheet1.Cells[row, 5].Value = item.RightEyeArea;
sheet1.Cells[row, 6].Value = item.BinocularArea;
sheet1.Cells[row, 7].Value = item.LowerVision;
sheet1.Cells[row, 8].Value = item.VisionRetentionRate;
sheet1.Cells[row, 9].Value = item.totalVisionArea;
sheet1.Cells[row, 10].Value = item.GetVisionRetentionRate;
row++;
}
sheet1.Cells[1, 1, row - 1, headers.Length].AutoFitColumns();
// ========== 插入柱状图展示最近5次左右眼面积对比==========
var records = TestDataStore.Records.TakeLast(5).ToList();
if (records.Count > 0)
{
int chartStartRow = row + 3;
sheet1.Cells[chartStartRow, 1].Value = "最近5次测试视野面积对比图";
sheet1.Cells[chartStartRow, 1].Style.Font.Bold = true;
int dataRowStart = chartStartRow + 1;
sheet1.Cells[dataRowStart, 1].Value = "序号";
sheet1.Cells[dataRowStart, 2].Value = "左眼面积";
sheet1.Cells[dataRowStart, 3].Value = "右眼面积";
for (int i = 0; i < records.Count; i++)
{
sheet1.Cells[dataRowStart + 1 + i, 1].Value = i + 1;
sheet1.Cells[dataRowStart + 1 + i, 2].Value = records[i].LeftEyeArea;
sheet1.Cells[dataRowStart + 1 + i, 3].Value = records[i].RightEyeArea;
}
var chart = sheet1.Drawings.AddChart("AreaChart", OfficeOpenXml.Drawing.Chart.eChartType.ColumnClustered);
chart.SetPosition(chartStartRow, 0, 0, 0);
chart.SetSize(600, 400);
chart.Title.Text = "左右眼视野面积对比";
chart.Legend.Position = OfficeOpenXml.Drawing.Chart.eLegendPosition.Bottom;
var series1 = chart.Series.Add(sheet1.Cells[dataRowStart + 1, 2, dataRowStart + records.Count, 2],
sheet1.Cells[dataRowStart + 1, 1, dataRowStart + records.Count, 1]);
series1.Header = "左眼面积";
var series2 = chart.Series.Add(sheet1.Cells[dataRowStart + 1, 3, dataRowStart + records.Count, 3],
sheet1.Cells[dataRowStart + 1, 1, dataRowStart + records.Count, 1]);
series2.Header = "右眼面积";
}
//// ========== 新增:三个饼图(左眼、右眼、双目)基于最新一次测试数据 ==========
//var latestRecord = TestDataStore.Records.LastOrDefault();
//if (latestRecord != null)
//{
// // 标准总面积(与 GetArea 中定义一致)
// double standardTotalArea = 2 * Math.PI * 330 * 330; // ≈ 684,000可根据实际调整
// // 如果您的 GetArea 中有公开的标准面积字段建议使用GetArea._standardTotalArea
// // 这里为了兼容,直接计算
// // 饼图数据区域起始行(放在柱状图右侧,可根据实际微调)
// int pieStartRow = row + 3;
// int pieStartColLeft = 8; // 左眼饼图起始列 H
// int pieStartColRight = 12; // 右眼饼图起始列 L
// int pieStartColBin = 16; // 双目饼图起始列 P
// // 左眼饼图数据
// double leftArea = latestRecord.LeftEyeArea;
// double leftRemain = standardTotalArea - leftArea;
// if (leftRemain < 0) leftRemain = 0;
// sheet1.Cells[pieStartRow, pieStartColLeft].Value = "左眼视野";
// sheet1.Cells[pieStartRow + 1, pieStartColLeft].Value = leftArea;
// sheet1.Cells[pieStartRow + 2, pieStartColLeft].Value = "剩余区域";
// sheet1.Cells[pieStartRow + 3, pieStartColLeft].Value = leftRemain;
// var pieLeft = sheet1.Drawings.AddChart("PieLeft", OfficeOpenXml.Drawing.Chart.eChartType.Pie);
// pieLeft.SetPosition(pieStartRow - 1, 0, pieStartColLeft - 2, 0);
// pieLeft.SetSize(300, 200);
// pieLeft.Title.Text = "左眼视野占比";
// pieLeft.Legend.Position = OfficeOpenXml.Drawing.Chart.eLegendPosition.Bottom;
// var seriesLeft = pieLeft.Series.Add(sheet1.Cells[pieStartRow + 1, pieStartColLeft, pieStartRow + 3, pieStartColLeft],
// sheet1.Cells[pieStartRow, pieStartColLeft, pieStartRow + 2, pieStartColLeft]);
// seriesLeft.Header = "面积 (cm²)";
// //这里需要加入百分比显示EPPlus 的饼图默认不显示百分比,需要手动设置数据标签
// // 右眼饼图数据
// double rightArea = latestRecord.RightEyeArea;
// double rightRemain = standardTotalArea - rightArea;
// if (rightRemain < 0) rightRemain = 0;
// sheet1.Cells[pieStartRow, pieStartColRight].Value = "右眼视野";
// sheet1.Cells[pieStartRow + 1, pieStartColRight].Value = rightArea;
// sheet1.Cells[pieStartRow + 2, pieStartColRight].Value = "剩余区域";
// sheet1.Cells[pieStartRow + 3, pieStartColRight].Value = rightRemain;
// var pieRight = sheet1.Drawings.AddChart("PieRight", OfficeOpenXml.Drawing.Chart.eChartType.Pie);
// pieRight.SetPosition(pieStartRow - 1, 0, pieStartColRight - 2, 0);
// pieRight.SetSize(300, 200);
// pieRight.Title.Text = "右眼视野占比";
// pieRight.Legend.Position = OfficeOpenXml.Drawing.Chart.eLegendPosition.Bottom;
// var seriesRight = pieRight.Series.Add(sheet1.Cells[pieStartRow + 1, pieStartColRight, pieStartRow + 3, pieStartColRight],
// sheet1.Cells[pieStartRow, pieStartColRight, pieStartRow + 2, pieStartColRight]);
// // 双目饼图数据
// double binArea = latestRecord.BinocularArea;
// double binRemain = standardTotalArea - binArea;
// if (binRemain < 0) binRemain = 0;
// sheet1.Cells[pieStartRow, pieStartColBin].Value = "双目视野";
// sheet1.Cells[pieStartRow + 1, pieStartColBin].Value = binArea;
// sheet1.Cells[pieStartRow + 2, pieStartColBin].Value = "剩余区域";
// sheet1.Cells[pieStartRow + 3, pieStartColBin].Value = binRemain;
// var pieBin = sheet1.Drawings.AddChart("PieBin", OfficeOpenXml.Drawing.Chart.eChartType.Pie);
// pieBin.SetPosition(pieStartRow - 1, 0, pieStartColBin - 2, 0);
// pieBin.SetSize(300, 200);
// pieBin.Title.Text = "双目视野占比";
// pieBin.Legend.Position = OfficeOpenXml.Drawing.Chart.eLegendPosition.Bottom;
// var seriesBin = pieBin.Series.Add(sheet1.Cells[pieStartRow + 1, pieStartColBin, pieStartRow + 3, pieStartColBin],
// sheet1.Cells[pieStartRow, pieStartColBin, pieStartRow + 2, pieStartColBin]);
//}
// 添加备注说明(位置下移避免覆盖饼图)
int noteRow = row + 30; // 原先是 row+20现在下移避免与饼图重叠
sheet1.Cells[noteRow, 1].Value = "备注:";
sheet1.Cells[noteRow, 2].Value = "1. 视野保存率公式依据 GB 2890-2022 计算;";
sheet1.Cells[noteRow + 1, 2].Value = "2. 空白视野面积为标准头模未戴面罩时的理论值;";
sheet1.Cells[noteRow + 2, 2].Value = "3. 测试过程中若出现异常,请参考原始记录。";
sheet1.Cells[noteRow, 1, noteRow + 2, 4].Style.Font.Size = 10;
sheet1.Cells[noteRow, 1, noteRow + 2, 4].Style.Font.Italic = true;
// ========== 工作表2原始数据明细 ==========
var sheet2 = package.Workbook.Worksheets.Add("原始数据");
for (int i = 0; i < headers.Length; i++)
sheet2.Cells[1, i + 1].Value = headers[i];
sheet2.Cells[1, 1, 1, headers.Length].Style.Font.Bold = true;
row = 2;
foreach (var item in TestDataStore.Records)
{
sheet2.Cells[row, 1].Value = item.Id;
sheet2.Cells[row, 2].Value = item.Date;
sheet2.Cells[row, 3].Value = item.Time;
sheet2.Cells[row, 4].Value = item.LeftEyeArea;
sheet2.Cells[row, 5].Value = item.RightEyeArea;
sheet2.Cells[row, 6].Value = item.BinocularArea;
sheet2.Cells[row, 7].Value = item.LowerVision;
sheet2.Cells[row, 8].Value = item.VisionRetentionRate;
sheet2.Cells[row, 9].Value = item.totalVisionArea;
sheet2.Cells[row, 10].Value = item.GetVisionRetentionRate;
row++;
}
sheet2.Cells[1, 1, row - 1, headers.Length].AutoFitColumns();
// 保存文件
package.SaveAs(new FileInfo(saveDialog.FileName));
MessageBox.Show($"报告已成功生成!\n路径{saveDialog.FileName}", "导出成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
catch (Exception ex)
{
MessageBox.Show($"生成报告失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
//进入页面是否要保留原来的数据????,
RecordDataGrid.ItemsSource = null;
RecordDataGrid.ItemsSource = TestDataStore.Records;
// 判断连接
if (!ModbusHelper.IsConnected)
{
MessageBox.Show("未连接");
return;
}
// 获取客户端
//var client = ModbusHelper.TcpClient;
}
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();
}
}