Files
huabe-sitondao/SLC1-N/Chart.cs
2025-11-14 16:12:32 +08:00

358 lines
15 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 Sunny.UI.Win32;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;
using System.Windows.Forms.DataVisualization;
using System.Windows.Forms.DataVisualization.Charting;
using System.Drawing.Imaging;
using System.Drawing;
using DocumentFormat.OpenXml.EMMA;
namespace SLC1_N
{
class Chart
{
// 生成泄漏量趋势图
public static void Create_TrendChart(int CH, string filepath, int maxProducts = 10)
{
try
{
string datatime = DateTime.Now.ToString("yyyyMMdd");
//string filepath = GetExcelFilePath(CH, datatime);
if (!File.Exists(filepath))
{
mxlLog.Instance.Error($"Excel文件不存在无法生成图表");
return;
}
// 读取Excel数据
var Exceldata = ReadExcelData(filepath, maxProducts);
if (Exceldata.Rows.Count == 0)
{
mxlLog.Instance.Error($"没有足够的数据生成趋势图");
return;
}
// 提取泄漏量数据
List<double> leakValues_list = new List<double>();
List<string> productLabels = new List<string>();
List<string> code_list = new List<string>();
List<string> results_list = new List<string>();
for (int i = 0; i < Exceldata.Rows.Count; i++)
{
string leakStr = Exceldata.Rows[i]["微漏泄漏量"].ToString();
Console.WriteLine($"Chart:{i}: {leakStr}");
// 移除单位,只保留数值
leakStr = leakStr.Replace("KPa", "")
.Replace("Pa/s", "")
.Replace("mbar/s", "")
.Replace("Pa", "")
.Replace("s", "").Trim();
Console.WriteLine($"Chart2:{i}: {leakStr}");
if (double.TryParse(leakStr, out double leakage))
{
leakValues_list.Add(leakage);
productLabels.Add($"产品{i + 1}");
code_list.Add(Exceldata.Rows[i]["条形码"].ToString());
results_list.Add(Exceldata.Rows[i]["测试结果"].ToString());
}
}
if (leakValues_list.Count == 0)
{
mxlLog.Instance.Error($"没有有效的泄漏量数据");
return;
}
// 创建图表图像
int width = 1000;
int height = 600;
using (Bitmap bitmap = new Bitmap(width, height))
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(Color.White);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; // 抗锯齿
// 设置边距
int margin = 50;
int chartWidth = width - 2 * margin;
int chartHeight = height - 2 * margin;
// 计算Y轴数据范围最大值加10%最小值减10%但不小于0
double maxValue = leakValues_list.Max() * 1.1;
double minValue = Math.Max(0, leakValues_list.Min() * 0.9);
// 绘制标题
using (Font titleFont = new Font("Arial", 16, FontStyle.Bold))
using (Brush titleBrush = new SolidBrush(Color.Black))
{
graphics.DrawString($"通道{CH} - 最近{maxProducts}个产品泄漏量趋势",
titleFont, titleBrush,
new PointF(width / 2 - 150, 10)); // 居中
}
// 绘制坐标轴Y轴坐标是向下的原点50550
using (Pen axisPen = new Pen(Color.Black, 2))
{
// X轴
graphics.DrawLine(axisPen, margin, height - margin, width - margin, height - margin);
// Y轴
graphics.DrawLine(axisPen, margin, margin, margin, height - margin);
}
// 绘制Y轴刻度
using (Font scaleFont = new Font("Arial", 8))
using (Brush scaleBrush = new SolidBrush(Color.Black))
{
for (int i = 0; i <= 5; i++) // 在 Y 轴上绘制 5 个等分刻度。
{
double value = minValue + (maxValue - minValue) * i / 5;
int y = height - margin - (int)(chartHeight * i / 5);
graphics.DrawString(value.ToString("F2"), scaleFont, scaleBrush,
margin - 45, y - 10); // 刻度标签坐标
}
}
// 绘制数据点
using (Pen dataPen = new Pen(Color.Blue, 2)) // 连线
using (Brush pointBrush = new SolidBrush(Color.Red)) // 坐标点
using (Font labelFont = new Font("Arial", 8))
using (Brush labelBrush = new SolidBrush(Color.DarkBlue)) // 数据点数值标签
{
for (int i = 0; i < leakValues_list.Count; i++)
{
double leakvalue = leakValues_list[i];
int x = margin + (int)(chartWidth * i / (leakValues_list.Count - 1));
int y = height - margin - (int)(chartHeight * (leakvalue - minValue) / (maxValue - minValue));
// 绘制数据点连接线
if (i > 0)
{
double prevValue = leakValues_list[i - 1]; // 前一个数据值
int prevX = margin + (int)(chartWidth * (i - 1) / (leakValues_list.Count - 1));
int prevY = height - margin - (int)(chartHeight * (prevValue - minValue) / (maxValue - minValue));
graphics.DrawLine(dataPen, prevX, prevY, x, y); // 连接前一个点和当前点
}
// 绘制数据点
graphics.FillEllipse(pointBrush, x - 4, y - 4, 8, 8);
// 绘制数值标签
string label = leakvalue.ToString("F2");
graphics.DrawString(label, labelFont, labelBrush, x - 15, y - 20);
// 绘制X轴标签
graphics.DrawString(productLabels[i], labelFont, labelBrush,
x - 10, height - margin + 10);
}
}
// 绘制图例
using (Font legendFont = new Font("Arial", 10))
using (Brush legendBrush = new SolidBrush(Color.Black))
{
graphics.DrawString("泄漏量趋势", legendFont, legendBrush, width - 150, margin);
graphics.DrawLine(new Pen(Color.Blue, 2), width - 170, margin + 10, width - 140, margin + 10);
graphics.FillEllipse(Brushes.Red, width - 145, margin + 6, 8, 8);
}
// 保存图表
string chartDir = Path.Combine(Path.GetDirectoryName(filepath), "Charts");
if (!Directory.Exists(chartDir))
Directory.CreateDirectory(chartDir);
string chartPath = Path.Combine(chartDir, $"CH{CH}_LeakageTrend_{datatime}.png");
bitmap.Save(chartPath, ImageFormat.Png);
//MessageBox.Show($"图表已保存至: {chartPath}");
}
}
catch (Exception ex)
{
mxlLog.Instance.Error($"图表生成错误: {ex.Message}", ex);
}
}
// 生成结果统计图
public static void Create_PieChart(int CH, string filepath)
{
try
{
string datatime = DateTime.Now.ToString("yyyyMMdd");
//string filepath = GetExcelFilePath(CH, datatime);
if (!File.Exists(filepath))
return;
var data = ReadExcelData(filepath, 1000);
int okCount = 0;
int ngCount = 0;
foreach (DataRow row in data.Rows)
{
string result = row["测试结果"].ToString();
if (result.Equals("OK", StringComparison.OrdinalIgnoreCase))
okCount++;
else if (result.Equals("NG", StringComparison.OrdinalIgnoreCase))
ngCount++;
}
int total = okCount + ngCount;
if (total == 0)
return;
// 创建统计图
int width = 600;
int height = 400;
using (Bitmap bitmap = new Bitmap(width, height))
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(Color.White);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// 绘制标题
using (Font titleFont = new Font("Arial", 16, FontStyle.Bold))
{
graphics.DrawString($"通道{CH} - 测试结果统计", titleFont, Brushes.Black, 150, 10);
graphics.DrawString($"总计: {total} 个产品", titleFont, Brushes.Black, 200, 40);
}
// 绘制饼图
int centerX = width / 2;
int centerY = height / 2 + 20;
int radius = 120;
if (okCount > 0)
{
float okAngle = 360f * okCount / total;
graphics.FillPie(Brushes.Green, centerX - radius, centerY - radius,
radius * 2, radius * 2, 0, okAngle);
}
if (ngCount > 0)
{
float ngAngle = 360f * ngCount / total;
graphics.FillPie(Brushes.Red, centerX - radius, centerY - radius,
radius * 2, radius * 2, 360f * okCount / total, ngAngle);
}
// 绘制图例
using (Font legendFont = new Font("Arial", 12))
{
graphics.FillRectangle(Brushes.Green, 100, height - 80, 20, 20);
graphics.DrawString($"OK: {okCount} ({okCount * 100f / total:F1}%)",
legendFont, Brushes.Black, 130, height - 80);
graphics.FillRectangle(Brushes.Red, 300, height - 80, 20, 20);
graphics.DrawString($"NG: {ngCount} ({ngCount * 100f / total:F1}%)",
legendFont, Brushes.Black, 330, height - 80);
}
// 保存图表
string chartDir = Path.Combine(Path.GetDirectoryName(filepath), "Charts");
if (!Directory.Exists(chartDir))
Directory.CreateDirectory(chartDir);
string chartPath = Path.Combine(chartDir, $"CH{CH}_ResultStat_{datatime}.png");
bitmap.Save(chartPath, ImageFormat.Png);
}
}
catch (Exception ex)
{
mxlLog.Instance.Error($"生成统计图错误: {ex.Message}", ex);
}
}
// 读取Excel数据
private static DataTable ReadExcelData(string filePath, int maxRows = 10)
{
DataTable dt = new DataTable();
Excel.Application xapp = null;
Excel.Workbook xbook = null;
try
{
xapp = new Excel.Application();
xapp.Visible = false;
xapp.DisplayAlerts = false;
xbook = xapp.Workbooks.Open(filePath);
Excel.Worksheet xsheet = (Excel.Worksheet)xbook.Sheets[1];
// 获取最后一行
int lastRow = xsheet.Cells.Find("*", System.Reflection.Missing.Value,
System.Reflection.Missing.Value,
System.Reflection.Missing.Value,
Excel.XlSearchOrder.xlByRows,
Excel.XlSearchDirection.xlPrevious,
false, System.Reflection.Missing.Value,
System.Reflection.Missing.Value).Row;
// 读取表头创建列
for (int col = 1; col <= 15; col++)
{
string header = (xsheet.Cells[1, col] as Excel.Range)?.Value2?.ToString() ?? $"Column{col}";
dt.Columns.Add(header);
}
// 读取数据从最后maxRows行开始
int startRow = Math.Max(2, lastRow - maxRows + 1);
for (int row = startRow; row <= lastRow; row++)
{
DataRow dr = dt.NewRow();
for (int col = 1; col <= 15; col++)
{
var cellValue = (xsheet.Cells[row, col] as Excel.Range)?.Value2;
dr[col - 1] = cellValue?.ToString() ?? "";
}
dt.Rows.Add(dr);
}
}
catch (Exception ex)
{
mxlLog.Instance.Error($"读取Excel数据错误", ex);
}
finally
{
if (xbook != null)
{
xbook.Close(false);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xbook);
}
if (xapp != null)
{
xapp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xapp);
}
GC.Collect();
GC.WaitForPendingFinalizers();
}
return dt;
}
// 获取Excel文件路径
private static string GetExcelFilePath(int CH, string date)
{
string basePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string chPath = CH == 1 ? "CH1" : "CH2";
return Path.Combine(basePath, chPath, $"{CH}_{date}.xls");
}
}
}