402 lines
18 KiB
C#
402 lines
18 KiB
C#
|
using MigraDoc.DocumentObjectModel;
|
|||
|
using MigraDoc.DocumentObjectModel.Tables;
|
|||
|
using MigraDoc.Rendering;
|
|||
|
using PdfSharp.Pdf;
|
|||
|
using PdfSharp.Pdf.IO;
|
|||
|
using Image = MigraDoc.DocumentObjectModel.Shapes.Image;
|
|||
|
|
|||
|
namespace AiQ_GUI
|
|||
|
{
|
|||
|
internal class PDF
|
|||
|
{
|
|||
|
public const string TestRecordDir = "G:\\Shared drives\\MAV Production Test Records\\AiQ\\";
|
|||
|
public const string ImageDir = $"{GoogleAPI.DrivePath}AiQ\\GUI's\\";
|
|||
|
public static void CreateFinalTestReport(Camera CamOnTest, string UserName, string fulltestvalues, DateTime PCTime)
|
|||
|
{
|
|||
|
// Create a new PDF document
|
|||
|
Document document = new();
|
|||
|
Section section = document.AddSection();
|
|||
|
|
|||
|
// Create a table with two columns
|
|||
|
Table latable = section.AddTable();
|
|||
|
latable.Borders.Visible = false; // No visible borders
|
|||
|
latable.AddColumn(Unit.FromCentimeter(13)); // Left column for MAV logo
|
|||
|
latable.AddColumn(Unit.FromCentimeter(3)); // Right column for AiQ logo
|
|||
|
|
|||
|
// Add a row for the logos
|
|||
|
Row larow = latable.AddRow();
|
|||
|
Cell cellMAV = larow.Cells[0];
|
|||
|
Cell cellAiQ = larow.Cells[1];
|
|||
|
|
|||
|
// Add MAV logo to the left cell
|
|||
|
Image imageMAV = cellMAV.AddImage($"{ImageDir}MAV-Logo.png");
|
|||
|
imageMAV.LockAspectRatio = true;
|
|||
|
imageMAV.Height = Unit.FromCentimeter(1); // Set height to 1 cm
|
|||
|
|
|||
|
// Add AiQ logo to the right cell
|
|||
|
Image imageAiQ = cellAiQ.AddImage($"{ImageDir}AiQ-Logo.png");
|
|||
|
imageAiQ.LockAspectRatio = true;
|
|||
|
imageAiQ.Height = Unit.FromCentimeter(1.25); // Set height to 1 cm
|
|||
|
|
|||
|
// Add spacing below the table
|
|||
|
Paragraph tableParagraph = section.AddParagraph();
|
|||
|
tableParagraph.Format.SpaceAfter = "0.5cm";
|
|||
|
|
|||
|
// Add Title
|
|||
|
Paragraph title = section.AddParagraph("Certificate of Conformity");
|
|||
|
title.Format.Font.Size = 20;
|
|||
|
title.Format.Font.Bold = true;
|
|||
|
title.Format.Alignment = ParagraphAlignment.Center;
|
|||
|
title.Format.SpaceAfter = "0.15cm";
|
|||
|
|
|||
|
// Add SubTitle
|
|||
|
string Subtitle = $"AiQ {CamOnTest.Model} {CamOnTest.Serial}" + (CamOnTest.RMANum != 0 ? $" (RMA {CamOnTest.RMANum})" : "");
|
|||
|
Paragraph subtitle = section.AddParagraph(Subtitle);
|
|||
|
subtitle.Format.Font.Size = 14;
|
|||
|
subtitle.Format.Alignment = ParagraphAlignment.Center;
|
|||
|
subtitle.Format.SpaceAfter = "0.5cm";
|
|||
|
|
|||
|
// Add Date
|
|||
|
Paragraph date = section.AddParagraph($"Date: {PCTime}{Environment.NewLine}Engineer: {UserName}{Environment.NewLine}Tested in accordance with MAV.146.060.010.01");
|
|||
|
date.Format.Font.Size = 10;
|
|||
|
date.Format.SpaceAfter = "0.5cm";
|
|||
|
|
|||
|
// Add Test Values header
|
|||
|
Paragraph valuesHeader = section.AddParagraph("Test Values:");
|
|||
|
valuesHeader.Format.Font.Size = 14;
|
|||
|
valuesHeader.Format.Font.Bold = true;
|
|||
|
valuesHeader.Format.SpaceAfter = "0.25cm";
|
|||
|
|
|||
|
// Add Test Values
|
|||
|
Paragraph FTV = section.AddParagraph(fulltestvalues);
|
|||
|
FTV.Format.Font.Size = 10;
|
|||
|
FTV.Format.SpaceAfter = "0.5cm";
|
|||
|
|
|||
|
// Add Images
|
|||
|
Paragraph imagesHeader = section.AddParagraph("Images:");
|
|||
|
imagesHeader.Format.Font.Size = 14;
|
|||
|
imagesHeader.Format.Font.Bold = true;
|
|||
|
imagesHeader.Format.SpaceAfter = "0.25cm";
|
|||
|
|
|||
|
// Create a table with two columns
|
|||
|
Table table = section.AddTable();
|
|||
|
table.Borders.Width = 0; // No border
|
|||
|
table.AddColumn(Unit.FromCentimeter(6)); // Column 1 width
|
|||
|
table.AddColumn(Unit.FromCentimeter(6)); // Column 2 width
|
|||
|
table.AddColumn(Unit.FromCentimeter(6)); // Column 3 width
|
|||
|
table.Rows.Alignment = RowAlignment.Center;
|
|||
|
|
|||
|
Row row = table.AddRow();
|
|||
|
|
|||
|
// Add IR F16 to the first column
|
|||
|
Cell cell1 = row.Cells[0];
|
|||
|
cell1.Format.Alignment = ParagraphAlignment.Center; // Center horizontally
|
|||
|
cell1.AddParagraph("Infrared - F16.0");
|
|||
|
Image imageIR = cell1.AddImage(LDS.MAVPath + LDS.IRTightsavePath);
|
|||
|
imageIR.LockAspectRatio = true; // Maintain aspect ratio
|
|||
|
imageIR.Width = Unit.FromCentimeter(6); // Scale as needed
|
|||
|
|
|||
|
// Add IR F2 to the second column
|
|||
|
Cell cell2 = row.Cells[1];
|
|||
|
cell2.Format.Alignment = ParagraphAlignment.Center; // Center horizontally
|
|||
|
cell2.AddParagraph("Infrared - F2.0");
|
|||
|
Image imageIR17 = cell2.AddImage(LDS.MAVPath + LDS.IROpensavePath);
|
|||
|
imageIR17.LockAspectRatio = true; // Maintain aspect ratio
|
|||
|
imageIR17.Width = Unit.FromCentimeter(6); // Scale as needed
|
|||
|
|
|||
|
// Add Image OV to the third column
|
|||
|
Cell cell3 = row.Cells[2];
|
|||
|
cell3.Format.Alignment = ParagraphAlignment.Center; // Center horizontally
|
|||
|
cell3.AddParagraph("Overview");
|
|||
|
Image imageOV = cell3.AddImage(LDS.MAVPath + LDS.OVsavePath);
|
|||
|
imageOV.LockAspectRatio = true; // Maintain aspect ratio
|
|||
|
imageOV.Width = Unit.FromCentimeter(6); // Scale as needed
|
|||
|
|
|||
|
section.AddParagraph().Format.SpaceAfter = "0.5cm";
|
|||
|
|
|||
|
// Add signiture
|
|||
|
Paragraph Signiture = section.AddParagraph("This unit is approved for shipment by:");
|
|||
|
Signiture.Format.Font.Size = 14;
|
|||
|
Signiture.Format.Alignment = ParagraphAlignment.Right;
|
|||
|
|
|||
|
Paragraph paragraph = section.AddParagraph();
|
|||
|
Image image = paragraph.AddImage($"{ImageDir}RP-Sig.jpg");
|
|||
|
image.Height = Unit.FromCentimeter(2);
|
|||
|
image.LockAspectRatio = true;
|
|||
|
paragraph.Format.Alignment = ParagraphAlignment.Right;
|
|||
|
|
|||
|
Paragraph PostSig = section.AddParagraph($"Richard Porter{Environment.NewLine}Head of Engineering{Environment.NewLine}MAV Systems Ltd");
|
|||
|
PostSig.Format.Font.Size = 14;
|
|||
|
PostSig.Format.Alignment = ParagraphAlignment.Right;
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
// Render PDF
|
|||
|
PdfDocumentRenderer renderer = new()
|
|||
|
{
|
|||
|
Document = document
|
|||
|
};
|
|||
|
renderer.RenderDocument();
|
|||
|
renderer.PdfDocument.Options.Layout = PdfWriterLayout.Compact;
|
|||
|
|
|||
|
// Adds RMA number to file namme if there is one
|
|||
|
string saveLoc = $"{TestRecordDir}{CamOnTest.Model}\\FinalTestReport_{CamOnTest.Model}_{CamOnTest.Serial}_{PCTime:dd-MM-yyyy_HH-mm-ss}" +
|
|||
|
(CamOnTest.RMANum != 0 ? $" RMA{CamOnTest.RMANum}" : "") + ".pdf";
|
|||
|
|
|||
|
if (!Directory.Exists($"{TestRecordDir}{CamOnTest.Model}\\")) // Does the model directory exist?
|
|||
|
{
|
|||
|
Directory.CreateDirectory($"{TestRecordDir}{CamOnTest.Model}\\"); // if not then create the directory now
|
|||
|
}
|
|||
|
|
|||
|
renderer.PdfDocument.Save(saveLoc);
|
|||
|
Logging.LogMessage("Final Test PDF saved to " + saveLoc);
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
// Show a message box to inform the user that PDF creation failed, displaying the exception message.
|
|||
|
MainForm.Instance.AddToActionsList($"Failed to create PDF:\n{ex.Message}");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static string CreateSoakTestReport(Camera CamSoak, string userName, DateTime pcTime, string logFilePath)
|
|||
|
{
|
|||
|
if (!File.Exists(logFilePath))
|
|||
|
{
|
|||
|
MainForm.Instance.AddToActionsList("Soak log file not found. Cannot create Soak Test Report.");
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
List<string> logLines = File.Exists(logFilePath) ? File.ReadAllLines(logFilePath).ToList() : new List<string>();
|
|||
|
|
|||
|
List<string> errorLines = logLines.Where(l => l.Contains("[ERROR]")).ToList();
|
|||
|
List<string> warningLines = logLines.Where(l => l.Contains("[WARNING]")).ToList();
|
|||
|
|
|||
|
// Create PDF document
|
|||
|
Document document = new();
|
|||
|
Section section = document.AddSection();
|
|||
|
|
|||
|
// Header table with logos
|
|||
|
Table logoTable = section.AddTable();
|
|||
|
logoTable.Borders.Visible = false;
|
|||
|
logoTable.AddColumn(Unit.FromCentimeter(13)); // Left column
|
|||
|
logoTable.AddColumn(Unit.FromCentimeter(3)); // Right column
|
|||
|
|
|||
|
Row logoRow = logoTable.AddRow();
|
|||
|
Cell cellMAV = logoRow.Cells[0];
|
|||
|
Cell cellAiQ = logoRow.Cells[1];
|
|||
|
|
|||
|
Image mavLogo = cellMAV.AddImage($"{ImageDir}MAV-Logo.png");
|
|||
|
mavLogo.LockAspectRatio = true;
|
|||
|
mavLogo.Height = Unit.FromCentimeter(1);
|
|||
|
|
|||
|
Image aiqLogo = cellAiQ.AddImage($"{ImageDir}AiQ-Logo.png");
|
|||
|
aiqLogo.LockAspectRatio = true;
|
|||
|
aiqLogo.Height = Unit.FromCentimeter(1.25);
|
|||
|
|
|||
|
section.AddParagraph().Format.SpaceAfter = "0.5cm";
|
|||
|
|
|||
|
// Title
|
|||
|
Paragraph title = section.AddParagraph("Soak Test Certificate");
|
|||
|
title.Format.Font.Size = 20;
|
|||
|
title.Format.Font.Bold = true;
|
|||
|
title.Format.Alignment = ParagraphAlignment.Center;
|
|||
|
title.Format.SpaceAfter = "0.15cm";
|
|||
|
|
|||
|
// Subtitle with RMA number if available
|
|||
|
string subtitleText = $"AiQ {CamSoak.Model} {CamSoak.Serial}" + (CamSoak.RMANum != 0 ? $" (RMA {CamSoak.RMANum})" : "");
|
|||
|
Paragraph subtitle = section.AddParagraph(subtitleText);
|
|||
|
subtitle.Format.Font.Size = 14;
|
|||
|
subtitle.Format.Alignment = ParagraphAlignment.Center;
|
|||
|
subtitle.Format.SpaceAfter = "0.5cm";
|
|||
|
|
|||
|
// Date and engineer info
|
|||
|
Paragraph datePara = section.AddParagraph($"Date: {pcTime:dd/MM/yyyy HH:mm:ss}{Environment.NewLine}Engineer: {userName}{Environment.NewLine}Tested in accordance with MAV.146.060.020.01");
|
|||
|
datePara.Format.Font.Size = 10;
|
|||
|
datePara.Format.SpaceAfter = "0.5cm";
|
|||
|
|
|||
|
// === Add warning/error summary with counts and color ===
|
|||
|
if (warningLines.Count > 0 || errorLines.Count > 0)
|
|||
|
{
|
|||
|
Paragraph logSummary = section.AddParagraph("Log Summary:");
|
|||
|
logSummary.Format.Font.Size = 12;
|
|||
|
logSummary.Format.Font.Bold = true;
|
|||
|
logSummary.Format.SpaceAfter = "0.2cm";
|
|||
|
|
|||
|
Paragraph totalCounts = section.AddParagraph();
|
|||
|
totalCounts.Format.Font.Size = 10;
|
|||
|
totalCounts.AddText("Total Errors: ");
|
|||
|
FormattedText errorCountText = totalCounts.AddFormattedText(errorLines.Count.ToString());
|
|||
|
errorCountText.Font.Color = Colors.Red;
|
|||
|
totalCounts.AddText("\nTotal Warnings: ");
|
|||
|
FormattedText warningCountText = totalCounts.AddFormattedText(warningLines.Count.ToString());
|
|||
|
warningCountText.Font.Color = Colors.Orange;
|
|||
|
totalCounts.Format.SpaceAfter = "0.2cm";
|
|||
|
|
|||
|
Dictionary<string, int> errorCounts = [];
|
|||
|
Dictionary<string, int> warningCounts = [];
|
|||
|
|
|||
|
foreach (string line in errorLines)
|
|||
|
{
|
|||
|
string message = ExtractLogMessageContent(line);
|
|||
|
if (errorCounts.TryGetValue(message, out int value))
|
|||
|
errorCounts[message] = ++value;
|
|||
|
else
|
|||
|
errorCounts[message] = 1;
|
|||
|
}
|
|||
|
|
|||
|
foreach (string line in warningLines)
|
|||
|
{
|
|||
|
string message = ExtractLogMessageContent(line);
|
|||
|
if (warningCounts.TryGetValue(message, out int value))
|
|||
|
warningCounts[message] = ++value;
|
|||
|
else
|
|||
|
warningCounts[message] = 1;
|
|||
|
}
|
|||
|
|
|||
|
foreach (KeyValuePair<string, int> ErrorCounter in errorCounts) // Itterates through the dictionary and Adds Errors and how many times that warning has appeared
|
|||
|
{
|
|||
|
Paragraph errorPara = section.AddParagraph();
|
|||
|
errorPara.Format.Font.Size = 9;
|
|||
|
errorPara.AddFormattedText("[", TextFormat.NotBold);
|
|||
|
FormattedText redText = errorPara.AddFormattedText("ERROR", TextFormat.NotBold);
|
|||
|
redText.Font.Color = Colors.Red;
|
|||
|
errorPara.AddFormattedText($"] {ErrorCounter.Key} (x{ErrorCounter.Value})");
|
|||
|
}
|
|||
|
|
|||
|
foreach (KeyValuePair<string, int> WarningCounter in warningCounts) // Itterates through the dictionary and Adds warning and how many times that warning has appeared
|
|||
|
{
|
|||
|
Paragraph warnPara = section.AddParagraph();
|
|||
|
warnPara.Format.Font.Size = 9;
|
|||
|
warnPara.AddFormattedText("[", TextFormat.NotBold);
|
|||
|
FormattedText orangeText = warnPara.AddFormattedText("WARNING", TextFormat.NotBold);
|
|||
|
orangeText.Font.Color = Colors.Orange;
|
|||
|
warnPara.AddFormattedText($"] {WarningCounter.Key} (x{WarningCounter.Value})");
|
|||
|
}
|
|||
|
|
|||
|
section.AddParagraph().Format.SpaceAfter = "0.5cm";
|
|||
|
}
|
|||
|
|
|||
|
// Signature
|
|||
|
if (errorLines.Count == 0)
|
|||
|
{
|
|||
|
Paragraph approval = section.AddParagraph("This unit is approved for shipment by:");
|
|||
|
approval.Format.Font.Size = 14;
|
|||
|
approval.Format.Alignment = ParagraphAlignment.Right;
|
|||
|
|
|||
|
Paragraph sigPara = section.AddParagraph();
|
|||
|
Image sigImage = sigPara.AddImage($"{ImageDir}RP-Sig.jpg");
|
|||
|
sigImage.Height = Unit.FromCentimeter(2);
|
|||
|
sigImage.LockAspectRatio = true;
|
|||
|
sigPara.Format.Alignment = ParagraphAlignment.Right;
|
|||
|
|
|||
|
Paragraph signoff = section.AddParagraph($"Richard Porter{Environment.NewLine}Head of Engineering{Environment.NewLine}MAV Systems Ltd");
|
|||
|
signoff.Format.Font.Size = 14;
|
|||
|
signoff.Format.Alignment = ParagraphAlignment.Right;
|
|||
|
}
|
|||
|
|
|||
|
// New page with full log
|
|||
|
Section logSection = document.AddSection();
|
|||
|
Paragraph fullLogTitle = logSection.AddParagraph("Full Log Output:");
|
|||
|
fullLogTitle.Format.Font.Size = 12;
|
|||
|
fullLogTitle.Format.Font.Bold = true;
|
|||
|
fullLogTitle.Format.SpaceAfter = "0.2cm";
|
|||
|
|
|||
|
foreach (string line in logLines)
|
|||
|
{
|
|||
|
Paragraph logLine = logSection.AddParagraph();
|
|||
|
logLine.Format.Font.Size = 8;
|
|||
|
|
|||
|
if (line.Contains("[ERROR]"))
|
|||
|
{
|
|||
|
int index = line.IndexOf("[ERROR]"); // You get rid of error only to add it back a few lines later? // this is because its difficukt to change the colour of text your importing its easier to remove and then make red
|
|||
|
logLine.AddText(line[..index]);
|
|||
|
logLine.AddFormattedText("ERROR", TextFormat.NotBold).Font.Color = Colors.Red;
|
|||
|
logLine.AddText(line[(index + "[ERROR]".Length)..]);
|
|||
|
}
|
|||
|
else if (line.Contains("[WARNING]"))
|
|||
|
{
|
|||
|
int index = line.IndexOf("[WARNING]"); // You get rid of warning only to add it back a few lines later? // Same here
|
|||
|
logLine.AddText(line[..index]);
|
|||
|
logLine.AddFormattedText("WARNING", TextFormat.NotBold).Font.Color = Colors.Orange;
|
|||
|
logLine.AddText(line[(index + "[WARNING]".Length)..]);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
logLine.AddText(line);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
// Render PDF
|
|||
|
PdfDocumentRenderer renderer = new()
|
|||
|
{
|
|||
|
Document = document
|
|||
|
};
|
|||
|
renderer.RenderDocument();
|
|||
|
renderer.PdfDocument.Options.Layout = PdfWriterLayout.Compact;
|
|||
|
|
|||
|
// Construct save path
|
|||
|
string fullPath = TestRecordDir + CamSoak.Model + $"\\SoakTestReport_{CamSoak.Model}_{CamSoak.Serial}_{pcTime:dd-MM-yyyy_HH-mm-ss}" + (CamSoak.RMANum != 0 ? $" RMA{CamSoak.RMANum}" : "") + ".pdf";
|
|||
|
|
|||
|
renderer.PdfDocument.Save(fullPath);
|
|||
|
Logging.LogMessage("Soak Test PDF saved to " + fullPath);
|
|||
|
return fullPath;
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
// Show a message box to inform the user that PDF creation failed, displaying the exception message.
|
|||
|
MainForm.Instance.AddToActionsList($"Failed to create PDF:\n{ex.Message}");
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private static string ExtractLogMessageContent(string line)
|
|||
|
{
|
|||
|
int tagEnd = line.IndexOf("] ");
|
|||
|
if (tagEnd != -1)
|
|||
|
{
|
|||
|
return line[(tagEnd + 2)..].Trim();
|
|||
|
}
|
|||
|
return line.Trim();
|
|||
|
}
|
|||
|
|
|||
|
public static bool LinkPDFs(string pdf1Path, string pdf2Path, string outputPath)
|
|||
|
{
|
|||
|
bool hasError = false;
|
|||
|
|
|||
|
if (!File.Exists(pdf1Path))
|
|||
|
{
|
|||
|
MainForm.Instance.AddToActionsList($"{pdf1Path} does not exist.");
|
|||
|
hasError = true;
|
|||
|
}
|
|||
|
|
|||
|
if (!File.Exists(pdf2Path))
|
|||
|
{
|
|||
|
MainForm.Instance.AddToActionsList($"{pdf2Path} does not exist.");
|
|||
|
hasError = true;
|
|||
|
}
|
|||
|
|
|||
|
if (hasError)
|
|||
|
return false;
|
|||
|
|
|||
|
using PdfDocument outputDocument = new(); // Create a new PDF document
|
|||
|
AppendPdf(outputDocument, pdf1Path);
|
|||
|
AppendPdf(outputDocument, pdf2Path);
|
|||
|
outputDocument.Save(outputPath); // Save the output document
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
public static void AppendPdf(PdfDocument target, string sourcePath)
|
|||
|
{
|
|||
|
using PdfDocument inputDocument = PdfReader.Open(sourcePath, PdfDocumentOpenMode.Import);
|
|||
|
|
|||
|
for (int idx = 0; idx < inputDocument.PageCount; idx++) // Iterate through all pages and add them to the output
|
|||
|
{
|
|||
|
PdfPage page = inputDocument.Pages[idx];
|
|||
|
target.AddPage(page);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|