358 lines
15 KiB
C#
358 lines
15 KiB
C#
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轴坐标是向下的,原点(50,550))
|
||
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");
|
||
}
|
||
}
|
||
|
||
}
|