Add Transmission Control Protocol file handling and update PCL serialization

- Introduced FileRead and Record classes for handling file reading in the Transmission Control Protocol.
- Enhanced Description, Detail, and other related classes with JSON serialization attributes for improved data handling.
- Implemented methods for reading and processing files, including network stream management.
- Updated unit tests to cover new functionality and ensure robust testing.
- Added new PDSF file handling classes and integrated them into the project structure.
- Refactored existing code to utilize source generation for JSON serialization, improving performance and maintainability.
This commit is contained in:
2025-09-15 09:59:54 -07:00
parent f717c6cf91
commit 0dc57eb3d7
34 changed files with 1815 additions and 209 deletions

View File

@ -0,0 +1,13 @@
namespace Adaptation.FileHandlers.pdsf;
internal class Constant
{
public string Id { get; } = "ID#";
public string Max { get; } = "Max:";
public string Min { get; } = "Min:";
public string Date { get; } = "Date:";
public string StdDev { get; } = "Std Dev:";
public string Average { get; } = "Average:";
}

View File

@ -0,0 +1,150 @@
using Adaptation.Shared;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace Adaptation.FileHandlers.pdsf;
internal class Convert
{
/// <summary>
/// Convert the raw data file to parsable file format - in this case from PCL to PDF
/// </summary>
/// <param name="sourceFile">source file to be converted to PDF</param>
/// <returns></returns>
private static string ConvertSourceFileToPdf(string ghostPCLFileName, Logistics logistics)
{
string result = Path.ChangeExtension(logistics.ReportFullPath, ".pdf");
if (!File.Exists(result))
{
//string arguments = string.Concat("-i \"", sourceFile, "\" -o \"", result, "\"");
string arguments = string.Concat("-dSAFER -dBATCH -dNOPAUSE -sOutputFile=\"", result, "\" -sDEVICE=pdfwrite \"", logistics.ReportFullPath, "\"");
//Process process = Process.Start(configData.LincPDFCFileName, arguments);
Process process = Process.Start(ghostPCLFileName, arguments);
_ = process.WaitForExit(30000);
if (!File.Exists(result))
throw new Exception("PDF file wasn't created");
}
return result;
}
private static Dictionary<string, string> PortableDocumentFormatSplit(string pdfTextStripperFileName, string sourcePath, string sourceFileNamePdf)
{
Dictionary<string, string> results = new();
ProcessStartInfo processStartInfo = new(pdfTextStripperFileName, $"s \"{sourceFileNamePdf}\"")
{
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
};
Process process = Process.Start(processStartInfo);
_ = process.WaitForExit(30000);
string text;
string checkFile;
string[] pdfFiles = Directory.GetFiles(sourcePath, "*.pdf", SearchOption.TopDirectoryOnly);
string[] textFiles = Directory.GetFiles(sourcePath, "*.txt", SearchOption.TopDirectoryOnly);
foreach (string pdfFile in pdfFiles)
{
if (pdfFile == sourceFileNamePdf)
continue;
checkFile = Path.ChangeExtension(pdfFile, ".txt");
if (!textFiles.Contains(checkFile))
continue;
text = File.ReadAllText(checkFile);
results.Add(pdfFile, text);
}
return results;
}
internal static ReadOnlyDictionary<string, string> PDF(Logistics logistics, string ghostPCLFileName, string pdfTextStripperFileName, List<FileInfo> fileInfoCollection)
{
Dictionary<string, string> results = new();
object item;
string pageText;
string pagePDFFile;
string pageTextFile;
List<string> sourceFiles = new();
string sourceFileNamePdf = ConvertSourceFileToPdf(ghostPCLFileName, logistics);
sourceFiles.Add(sourceFileNamePdf);
string sourcePath = Path.GetDirectoryName(logistics.ReportFullPath) ?? throw new Exception();
string sourceFileNameWithoutExtension = Path.GetFileNameWithoutExtension(logistics.ReportFullPath);
string[] txtFiles = Directory.GetFiles(sourcePath, $"{sourceFileNameWithoutExtension}_*.txt", SearchOption.TopDirectoryOnly);
if (txtFiles.Length != 0)
{
txtFiles = (from l in txtFiles orderby l.Length, l select l).ToArray();
foreach (string txtFile in txtFiles)
{
sourceFiles.Add(txtFile);
pageText = File.ReadAllText(txtFile);
pagePDFFile = Path.ChangeExtension(txtFile, ".pdf");
if (!File.Exists(pagePDFFile))
continue;
results.Add(pagePDFFile, pageText);
}
}
if (results.Count == 0)
{
try
{
java.io.File file = new(sourceFileNamePdf);
org.apache.pdfbox.util.Splitter splitter = new();
org.apache.pdfbox.pdmodel.PDDocument pdDocument = org.apache.pdfbox.pdmodel.PDDocument.load(file);
java.util.List list = splitter.split(pdDocument);
java.util.ListIterator iterator = list.listIterator();
org.apache.pdfbox.util.PDFTextStripper dataStripper = new();
for (short i = 1; i < short.MaxValue; i++)
{
if (!iterator.hasNext())
break;
item = iterator.next();
pagePDFFile = string.Concat(sourcePath, @"\", sourceFileNameWithoutExtension, "_", i, ".pdf");
pageTextFile = Path.ChangeExtension(pagePDFFile, ".txt");
if (File.Exists(pageTextFile))
{
pageText = File.ReadAllText(pageTextFile);
sourceFiles.Add(pageTextFile);
if (item is not org.apache.pdfbox.pdmodel.PDDocument pd)
continue;
pd.close();
}
else if (File.Exists(pagePDFFile))
{
org.apache.pdfbox.pdmodel.PDDocument document = org.apache.pdfbox.pdmodel.PDDocument.load(pagePDFFile);
pageText = dataStripper.getText(document);
document.close();
sourceFiles.Add(pagePDFFile);
if (item is not org.apache.pdfbox.pdmodel.PDDocument pd)
continue;
pd.close();
}
else
{
if (item is not org.apache.pdfbox.pdmodel.PDDocument pd)
continue;
pageText = dataStripper.getText(pd);
pd.save(pagePDFFile);
sourceFiles.Add(pagePDFFile);
pd.close();
File.WriteAllText(pageTextFile, pageText);
sourceFiles.Add(pageTextFile);
}
results.Add(pagePDFFile, pageText);
}
pdDocument.close();
}
catch (MissingMethodException)
{
if (results.Count == 0)
results = PortableDocumentFormatSplit(pdfTextStripperFileName, sourcePath, sourceFileNamePdf);
}
}
foreach (string sourceFile in sourceFiles)
fileInfoCollection.Add(new FileInfo(sourceFile));
return new(results);
}
}

View File

@ -0,0 +1,136 @@
using Adaptation.Eaf.Management.ConfigurationData.CellAutomation;
using Adaptation.Ifx.Eaf.EquipmentConnector.File.Configuration;
using Adaptation.Shared;
using Adaptation.Shared.Duplicator;
using Adaptation.Shared.Methods;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Text.Json;
namespace Adaptation.FileHandlers.pdsf;
public class FileRead : Shared.FileRead, IFileRead
{
private readonly string _GhostPCLFileName;
private readonly string _PDFTextStripperFileName;
public FileRead(ISMTP smtp, Dictionary<string, string> fileParameter, string cellInstanceName, int? connectionCount, string cellInstanceConnectionName, FileConnectorConfiguration fileConnectorConfiguration, string equipmentTypeName, string parameterizedModelObjectDefinitionType, IList<ModelObjectParameterDefinition> modelObjectParameters, string equipmentDictionaryName, Dictionary<string, List<long>> dummyRuns, Dictionary<long, List<Shared.Metrology.WS.Results>> staticRuns, bool useCyclicalForDescription, bool isEAFHosted) :
base(new Description(), false, smtp, fileParameter, cellInstanceName, connectionCount, cellInstanceConnectionName, fileConnectorConfiguration, equipmentTypeName, parameterizedModelObjectDefinitionType, modelObjectParameters, equipmentDictionaryName, dummyRuns, staticRuns, useCyclicalForDescription, isEAFHosted: connectionCount is null)
{
_MinFileLength = 15;
_NullData = string.Empty;
_Logistics = new(this);
if (_FileParameter is null)
throw new Exception(cellInstanceConnectionName);
if (_ModelObjectParameterDefinitions is null)
throw new Exception(cellInstanceConnectionName);
if (_IsDuplicator)
throw new Exception(cellInstanceConnectionName);
_GhostPCLFileName = Path.Combine(AppContext.BaseDirectory, "gpcl6win64.exe");
if (!File.Exists(_GhostPCLFileName))
throw new Exception("Ghost PCL FileName doesn't Exist!");
_PDFTextStripperFileName = Path.Combine(AppContext.BaseDirectory, "PDF-Text-Stripper.exe");
if (!File.Exists(_PDFTextStripperFileName))
throw new Exception("PDF-Text-Stripper FileName doesn't Exist!");
if (_IsEAFHosted)
NestExistingFiles(_FileConnectorConfiguration);
}
void IFileRead.Move(Tuple<string, Test[], JsonElement[], List<FileInfo>> extractResults, Exception exception) => Move(extractResults);
void IFileRead.WaitForThread() => WaitForThread(thread: null, threadExceptions: null);
string IFileRead.GetEventDescription()
{
string result = _Description.GetEventDescription();
return result;
}
List<string> IFileRead.GetHeaderNames()
{
List<string> results = _Description.GetHeaderNames();
return results;
}
string[] IFileRead.Move(Tuple<string, Test[], JsonElement[], List<FileInfo>> extractResults, string to, string from, string resolvedFileLocation, Exception exception)
{
string[] results = Move(extractResults, to, from, resolvedFileLocation, exception);
return results;
}
JsonProperty[] IFileRead.GetDefault()
{
JsonProperty[] results = _Description.GetDefault(this, _Logistics);
return results;
}
Dictionary<string, string> IFileRead.GetDisplayNamesJsonElement()
{
Dictionary<string, string> results = _Description.GetDisplayNamesJsonElement(this);
return results;
}
List<IDescription> IFileRead.GetDescriptions(IFileRead fileRead, List<Test> tests, IProcessData processData)
{
List<IDescription> results = _Description.GetDescriptions(fileRead, _Logistics, tests, processData);
return results;
}
Tuple<string, Test[], JsonElement[], List<FileInfo>> IFileRead.GetExtractResult(string reportFullPath, string eventName)
{
Tuple<string, Test[], JsonElement[], List<FileInfo>> results;
if (string.IsNullOrEmpty(eventName))
throw new Exception();
_ReportFullPath = reportFullPath;
DateTime dateTime = DateTime.Now;
results = GetExtractResult(reportFullPath, dateTime);
if (results.Item3 is null)
results = new Tuple<string, Test[], JsonElement[], List<FileInfo>>(results.Item1, Array.Empty<Test>(), JsonSerializer.Deserialize<JsonElement[]>("[]"), results.Item4);
if (results.Item3.Length > 0 && _IsEAFHosted)
WritePDSF(this, results.Item3);
UpdateLastTicksDuration(DateTime.Now.Ticks - dateTime.Ticks);
return results;
}
Tuple<string, Test[], JsonElement[], List<FileInfo>> IFileRead.ReExtract()
{
Tuple<string, Test[], JsonElement[], List<FileInfo>> results;
List<string> headerNames = _Description.GetHeaderNames();
Dictionary<string, string> keyValuePairs = _Description.GetDisplayNamesJsonElement(this);
results = ReExtract(this, headerNames, keyValuePairs);
return results;
}
#nullable enable
private Tuple<string, Test[], JsonElement[], List<FileInfo>> GetExtractResult(string reportFullPath, DateTime dateTime)
{
Tuple<string, Test[], JsonElement[], List<FileInfo>> results;
string result;
JsonElement[] jsonElements;
Test[] tests = Array.Empty<Test>();
List<FileInfo> fileInfoCollection = new();
ProcessDataStandardFormat processDataStandardFormat = ProcessDataStandardFormat.GetProcessDataStandardFormat(reportFullPath);
_Logistics = new Logistics(reportFullPath, processDataStandardFormat);
SetFileParameterLotIDToLogisticsMID();
ReadOnlyDictionary<string, string> pages = Convert.PDF(_Logistics, _GhostPCLFileName, _PDFTextStripperFileName, fileInfoCollection);
Run? run = Run.Get(_Logistics, fileInfoCollection, pages);
if (run is null)
{
jsonElements = Array.Empty<JsonElement>();
result = string.Concat("A) No Data - ", dateTime.Ticks);
results = new(result, tests, jsonElements, fileInfoCollection);
}
else
{
result = string.Join(Environment.NewLine, _Logistics.Logistics1);
jsonElements = _IsEAFHosted ? Array.Empty<JsonElement>() : ProcessDataStandardFormat.GetArray(processDataStandardFormat);
results = new(result, tests, jsonElements, fileInfoCollection);
}
return results;
}
}

View File

@ -0,0 +1,322 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text.Json.Serialization;
namespace Adaptation.FileHandlers.pdsf;
#nullable enable
public class Header
{
public Header(string date,
string recipe,
string id,
ReadOnlyCollection<WaferSummary> waferSummary,
string lPDCountMin,
string lPDCM2Min,
string areaCountMin,
string areaTotalMin,
string scratchCountMin,
string scratchTotalMin,
string sumOfDefectsMin,
string hazeRegionMin,
string hazeAverageMin,
string lPDCountMax,
string lPDCM2Max,
string areaCountMax,
string areaTotalMax,
string scratchCountMax,
string scratchTotalMax,
string sumOfDefectsMax,
string hazeRegionMax,
string hazeAverageMax,
string lPDCountAvg,
string lPDCM2Avg,
string areaCountAvg,
string areaTotalAvg,
string scratchCountAvg,
string scratchTotalAvg,
string sumOfDefectsAvg,
string hazeRegionAvg,
string hazeAverageAvg,
string lPDCountStdDev,
string lPDCM2StdDev,
string areaCountStdDev,
string areaTotalStdDev,
string scratchCountStdDev,
string scratchTotalStdDev,
string sumOfDefectsStdDev,
string hazeRegionStdDev,
string hazeAverageStdDev)
{
Date = date;
Recipe = recipe;
Id = id;
WaferSummary = waferSummary;
LPDCountMin = lPDCountMin;
LPDCM2Min = lPDCM2Min;
AreaCountMin = areaCountMin;
AreaTotalMin = areaTotalMin;
ScratchCountMin = scratchCountMin;
ScratchTotalMin = scratchTotalMin;
SumOfDefectsMin = sumOfDefectsMin;
HazeRegionMin = hazeRegionMin;
HazeAverageMin = hazeAverageMin;
LPDCountMax = lPDCountMax;
LPDCM2Max = lPDCM2Max;
AreaCountMax = areaCountMax;
AreaTotalMax = areaTotalMax;
ScratchCountMax = scratchCountMax;
ScratchTotalMax = scratchTotalMax;
SumOfDefectsMax = sumOfDefectsMax;
HazeRegionMax = hazeRegionMax;
HazeAverageMax = hazeAverageMax;
LPDCountAvg = lPDCountAvg;
LPDCM2Avg = lPDCM2Avg;
AreaCountAvg = areaCountAvg;
AreaTotalAvg = areaTotalAvg;
ScratchCountAvg = scratchCountAvg;
ScratchTotalAvg = scratchTotalAvg;
SumOfDefectsAvg = sumOfDefectsAvg;
HazeRegionAvg = hazeRegionAvg;
HazeAverageAvg = hazeAverageAvg;
LPDCountStdDev = lPDCountStdDev;
LPDCM2StdDev = lPDCM2StdDev;
AreaCountStdDev = areaCountStdDev;
AreaTotalStdDev = areaTotalStdDev;
ScratchCountStdDev = scratchCountStdDev;
ScratchTotalStdDev = scratchTotalStdDev;
SumOfDefectsStdDev = sumOfDefectsStdDev;
HazeRegionStdDev = hazeRegionStdDev;
HazeAverageStdDev = hazeAverageStdDev;
}
public string Date { get; }
public string Recipe { get; }
public string Id { get; }
public ReadOnlyCollection<WaferSummary> WaferSummary { get; }
public string LPDCountMin { get; }
public string LPDCM2Min { get; }
public string AreaCountMin { get; }
public string AreaTotalMin { get; }
public string ScratchCountMin { get; }
public string ScratchTotalMin { get; }
public string SumOfDefectsMin { get; }
public string HazeRegionMin { get; }
public string HazeAverageMin { get; }
public string LPDCountMax { get; }
public string LPDCM2Max { get; }
public string AreaCountMax { get; }
public string AreaTotalMax { get; }
public string ScratchCountMax { get; }
public string ScratchTotalMax { get; }
public string SumOfDefectsMax { get; }
public string HazeRegionMax { get; }
public string HazeAverageMax { get; }
public string LPDCountAvg { get; }
public string LPDCM2Avg { get; }
public string AreaCountAvg { get; }
public string AreaTotalAvg { get; }
public string ScratchCountAvg { get; }
public string ScratchTotalAvg { get; }
public string SumOfDefectsAvg { get; }
public string HazeRegionAvg { get; }
public string HazeAverageAvg { get; }
public string LPDCountStdDev { get; }
public string LPDCM2StdDev { get; }
public string AreaCountStdDev { get; }
public string AreaTotalStdDev { get; }
public string ScratchCountStdDev { get; }
public string ScratchTotalStdDev { get; }
public string SumOfDefectsStdDev { get; }
public string HazeRegionStdDev { get; }
public string HazeAverageStdDev { get; }
private static ReadOnlyCollection<string> FixToEolArray(string[] toEol)
{
List<string> results = new();
const int MAX_COLUMNS = 9;
if (toEol.Length >= MAX_COLUMNS)
results.AddRange(toEol);
else
{
string leftVal, rightVal;
List<string> toEolList = new(toEol);
int[] mColumnWidths = new int[MAX_COLUMNS] { 8, 6, 6, 6, 6, 7, 7, 5, 7 };
if (string.IsNullOrEmpty(toEolList[toEolList.Count - 1]))
toEolList.RemoveAt(toEolList.Count - 1);
for (int i = toEolList.Count; i < MAX_COLUMNS; i++)
toEolList.Insert(0, "");
for (int i = MAX_COLUMNS - 1; i >= 0; i--)
{
if (toEolList[i].Length > mColumnWidths[i])
{
leftVal = toEolList[i].Substring(0, toEolList[i].Length - mColumnWidths[i]);
rightVal = toEolList[i].Substring(leftVal.Length);
toEolList[i] = rightVal;
toEolList.Insert(i, leftVal);
if (string.IsNullOrEmpty(toEolList[0]))
toEolList.RemoveAt(0);
}
}
results.AddRange(toEolList);
}
return results.AsReadOnly();
}
internal static void ScanPast(string text, int[] i, string search)
{
int num = text.IndexOf(search, i[0]);
if (num > -1)
i[0] = num + search.Length;
else
i[0] = text.Length;
}
internal static string GetBefore(string text, int[] i, string search)
{
int num = text.IndexOf(search, i[0]);
if (num > -1)
{
string str = text.Substring(i[0], num - i[0]);
i[0] = num + search.Length;
return str.Trim();
}
string str1 = text.Substring(i[0]);
i[0] = text.Length;
return str1.Trim();
}
private static string GetBefore(string text, int[] i, string search, bool trim)
{
if (trim)
return GetBefore(text, i, search);
int num = text.IndexOf(search, i[0]);
if (num > -1)
{
string str = text.Substring(i[0], num - i[0]);
i[0] = num + search.Length;
return str;
}
string str1 = text.Substring(i[0]);
i[0] = text.Length;
return str1;
}
internal static string GetToEOL(string text, int[] i) =>
GetBefore(text, i, "\n");
private static string GetToEOL(string text, int[] i, bool trim)
{
if (trim)
return GetToEOL(text, i);
return GetBefore(text, i, "\n", false);
}
internal static Header Get(ReadOnlyDictionary<string, string> pages, Constant constant, string headerFileName)
{
Header? result;
string id;
string? text;
string[] segmentsB;
string[] segmentsC;
int[] i = new int[] { 0 };
WaferSummary waferSummary;
List<WaferSummary> collection = new();
if (!pages.TryGetValue(headerFileName, out text))
throw new Exception();
ScanPast(text, i, constant.Date);
string date = GetToEOL(text, i);
ScanPast(text, i, "Recipe ID:");
string recipe = GetBefore(text, i, "LotID:");
recipe = recipe.Replace(";", "");
if (text.Contains("[]"))
id = GetBefore(text, i, "[]");
else if (text.Contains("[7]"))
id = GetBefore(text, i, "[7]");
else
id = GetBefore(text, i, "[");
ScanPast(text, i, "*");
string[] segments = text.Substring(i[0]).Split('*');
string[] split = new string[] { Environment.NewLine };
foreach (string segment in segments)
{
segmentsB = segment.Split(split, StringSplitOptions.None);
segmentsC = segmentsB[0].Split(' ');
waferSummary = new(id: segmentsC.Length < 1 ? string.Empty : segmentsC[0].Trim(),
lPDCount: segmentsC.Length < 2 ? string.Empty : segmentsC[1].Trim(),
lPDCM2: segmentsC.Length < 3 ? string.Empty : segmentsC[2].Trim(),
areaCount: segmentsC.Length < 4 ? string.Empty : segmentsC[3].Trim(),
areaTotal: segmentsC.Length < 5 ? string.Empty : segmentsC[4].Trim(),
scratchCount: segmentsC.Length < 6 ? string.Empty : segmentsC[5].Trim(),
scratchTotal: segmentsC.Length < 7 ? string.Empty : segmentsC[6].Trim(),
sumOfDefects: segmentsC.Length < 8 ? string.Empty : segmentsC[7].Trim(),
hazeRegion: segmentsC.Length < 9 ? string.Empty : segmentsC[8].Trim(),
hazeAverage: segmentsC.Length < 10 ? string.Empty : segmentsC[9].Trim(),
grade: segmentsC.Length < 11 ? string.Empty : segmentsC[10].Trim());
collection.Add(waferSummary);
}
ScanPast(text, i, constant.Min);
string[] preToEol1 = GetToEOL(text, i, false).Trim().Split(' ');
ReadOnlyCollection<string> toEol1 = FixToEolArray(preToEol1);
ScanPast(text, i, constant.Max);
string[] preToEol2 = GetToEOL(text, i, false).Trim().Split(' ');
ReadOnlyCollection<string> toEol2 = FixToEolArray(preToEol2);
ScanPast(text, i, constant.Average);
string[] preToEol3 = GetToEOL(text, i, false).Trim().Split(' ');
ReadOnlyCollection<string> toEol3 = FixToEolArray(preToEol3);
ScanPast(text, i, constant.StdDev);
string[] preToEol4 = GetToEOL(text, i, false).Trim().Split(' ');
ReadOnlyCollection<string> toEol4 = FixToEolArray(preToEol4);
result = new(date: date,
recipe: recipe,
id: id,
waferSummary: collection.AsReadOnly(),
lPDCountMin: toEol1[0].Trim(),
lPDCM2Min: toEol1[1].Trim(),
areaCountMin: toEol1[2].Trim(),
areaTotalMin: toEol1[3].Trim(),
scratchCountMin: toEol1[4].Trim(),
scratchTotalMin: toEol1[5].Trim(),
sumOfDefectsMin: toEol1[6].Trim(),
hazeRegionMin: toEol1[7].Trim(),
hazeAverageMin: toEol1[8].Trim(),
lPDCountMax: toEol2[0].Trim(),
lPDCM2Max: toEol2[1].Trim(),
areaCountMax: toEol2[2].Trim(),
areaTotalMax: toEol2[3].Trim(),
scratchCountMax: toEol2[4].Trim(),
scratchTotalMax: toEol2[5].Trim(),
sumOfDefectsMax: toEol2[6].Trim(),
hazeRegionMax: toEol2[7].Trim(),
hazeAverageMax: toEol2[8].Trim(),
lPDCountAvg: toEol3[0].Trim(),
lPDCM2Avg: toEol3[1].Trim(),
areaCountAvg: toEol3[2].Trim(),
areaTotalAvg: toEol3[3].Trim(),
scratchCountAvg: toEol3[4].Trim(),
scratchTotalAvg: toEol3[5].Trim(),
sumOfDefectsAvg: toEol3[6].Trim(),
hazeRegionAvg: toEol3[7].Trim(),
hazeAverageAvg: toEol3[8].Trim(),
lPDCountStdDev: toEol4[0].Trim(),
lPDCM2StdDev: toEol4[1].Trim(),
areaCountStdDev: toEol4[2].Trim(),
areaTotalStdDev: toEol4[3].Trim(),
scratchCountStdDev: toEol4[4].Trim(),
scratchTotalStdDev: toEol4[5].Trim(),
sumOfDefectsStdDev: toEol4[6].Trim(),
hazeRegionStdDev: toEol4[7].Trim(),
hazeAverageStdDev: toEol4[8].Trim());
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Header))]
internal partial class HeaderSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,193 @@
using System.Text.Json.Serialization;
namespace Adaptation.FileHandlers.pdsf;
#nullable enable
internal class Row
{
public Row(Run run, int i)
{
Index = i;
//
Date = run.Header.Date;
Recipe = run.Header.Recipe;
Id = run.Header.Id;
//
WaferId = run.Header.WaferSummary[i].Id;
LPDCount = run.Header.WaferSummary[i].LPDCount;
LPDCM2 = run.Header.WaferSummary[i].LPDCM2;
AreaCount = run.Header.WaferSummary[i].AreaCount;
AreaTotal = run.Header.WaferSummary[i].AreaTotal;
ScratchCount = run.Header.WaferSummary[i].ScratchCount;
ScratchTotal = run.Header.WaferSummary[i].ScratchTotal;
SumOfDefects = run.Header.WaferSummary[i].SumOfDefects;
HazeRegion = run.Header.WaferSummary[i].HazeRegion;
HazeAverage = run.Header.WaferSummary[i].HazeAverage;
Grade = run.Header.WaferSummary[i].Grade;
//
LPDCountMin = run.Header.LPDCountMin;
LPDCM2Min = run.Header.LPDCM2Min;
AreaCountMin = run.Header.AreaCountMin;
AreaTotalMin = run.Header.AreaTotalMin;
ScratchCountMin = run.Header.ScratchCountMin;
ScratchTotalMin = run.Header.ScratchTotalMin;
SumOfDefectsMin = run.Header.SumOfDefectsMin;
HazeRegionMin = run.Header.HazeRegionMin;
HazeAverageMin = run.Header.HazeAverageMin;
LPDCountMax = run.Header.LPDCountMax;
LPDCM2Max = run.Header.LPDCM2Max;
AreaCountMax = run.Header.AreaCountMax;
AreaTotalMax = run.Header.AreaTotalMax;
ScratchCountMax = run.Header.ScratchCountMax;
ScratchTotalMax = run.Header.ScratchTotalMax;
SumOfDefectsMax = run.Header.SumOfDefectsMax;
HazeRegionMax = run.Header.HazeRegionMax;
HazeAverageMax = run.Header.HazeAverageMax;
LPDCountAvg = run.Header.LPDCountAvg;
LPDCM2Avg = run.Header.LPDCM2Avg;
AreaCountAvg = run.Header.AreaCountAvg;
AreaTotalAvg = run.Header.AreaTotalAvg;
ScratchCountAvg = run.Header.ScratchCountAvg;
ScratchTotalAvg = run.Header.ScratchTotalAvg;
SumOfDefectsAvg = run.Header.SumOfDefectsAvg;
HazeRegionAvg = run.Header.HazeRegionAvg;
HazeAverageAvg = run.Header.HazeAverageAvg;
LPDCountStdDev = run.Header.LPDCountStdDev;
LPDCM2StdDev = run.Header.LPDCM2StdDev;
AreaCountStdDev = run.Header.AreaCountStdDev;
AreaTotalStdDev = run.Header.AreaTotalStdDev;
ScratchCountStdDev = run.Header.ScratchCountStdDev;
ScratchTotalStdDev = run.Header.ScratchTotalStdDev;
SumOfDefectsStdDev = run.Header.SumOfDefectsStdDev;
HazeRegionStdDev = run.Header.HazeRegionStdDev;
HazeAverageStdDev = run.Header.HazeAverageStdDev;
//
WaferDate = run.Wafers[i].Date;
Comments = run.Wafers[i].Comments;
Sort = run.Wafers[i].Sort;
WaferLPDCount = run.Wafers[i].LPDCount;
WaferLPDCM2 = run.Wafers[i].LPDCM2;
Bin1 = run.Wafers[i].Bin1;
Bin2 = run.Wafers[i].Bin2;
Bin3 = run.Wafers[i].Bin3;
Bin4 = run.Wafers[i].Bin4;
Bin5 = run.Wafers[i].Bin5;
Bin6 = run.Wafers[i].Bin6;
Bin7 = run.Wafers[i].Bin7;
Bin8 = run.Wafers[i].Bin8;
Mean = run.Wafers[i].Mean;
StdDev = run.Wafers[i].StdDev;
WaferAreaCount = run.Wafers[i].AreaCount;
WaferAreaTotal = run.Wafers[i].AreaTotal;
WaferScratchCount = run.Wafers[i].ScratchCount;
WaferScratchTotal = run.Wafers[i].ScratchTotal;
WaferSumOfDefects = run.Wafers[i].SumOfDefects;
WaferHazeRegion = run.Wafers[i].HazeRegion;
WaferHazeAverage = run.Wafers[i].HazeAverage;
HazePeak = run.Wafers[i].HazePeak;
Laser = run.Wafers[i].Laser;
Gain = run.Wafers[i].Gain;
Diameter = run.Wafers[i].Diameter;
Thresh = run.Wafers[i].Thresh;
Exclusion = run.Wafers[i].Exclusion;
HazeRng = run.Wafers[i].HazeRng;
Thruput = run.Wafers[i].Thruput;
WaferRecipe = run.Wafers[i].Recipe;
}
public int Index { get; }
//
public string Date { get; }
public string Recipe { get; }
public string Id { get; }
//
public string WaferId { get; }
public string LPDCount { get; }
public string LPDCM2 { get; }
public string AreaCount { get; }
public string AreaTotal { get; }
public string ScratchCount { get; }
public string ScratchTotal { get; }
public string SumOfDefects { get; }
public string HazeRegion { get; }
public string HazeAverage { get; }
public string Grade { get; }
//
public string LPDCountMin { get; }
public string LPDCM2Min { get; }
public string AreaCountMin { get; }
public string AreaTotalMin { get; }
public string ScratchCountMin { get; }
public string ScratchTotalMin { get; }
public string SumOfDefectsMin { get; }
public string HazeRegionMin { get; }
public string HazeAverageMin { get; }
public string LPDCountMax { get; }
public string LPDCM2Max { get; }
public string AreaCountMax { get; }
public string AreaTotalMax { get; }
public string ScratchCountMax { get; }
public string ScratchTotalMax { get; }
public string SumOfDefectsMax { get; }
public string HazeRegionMax { get; }
public string HazeAverageMax { get; }
public string LPDCountAvg { get; }
public string LPDCM2Avg { get; }
public string AreaCountAvg { get; }
public string AreaTotalAvg { get; }
public string ScratchCountAvg { get; }
public string ScratchTotalAvg { get; }
public string SumOfDefectsAvg { get; }
public string HazeRegionAvg { get; }
public string HazeAverageAvg { get; }
public string LPDCountStdDev { get; }
public string LPDCM2StdDev { get; }
public string AreaCountStdDev { get; }
public string AreaTotalStdDev { get; }
public string ScratchCountStdDev { get; }
public string ScratchTotalStdDev { get; }
public string SumOfDefectsStdDev { get; }
public string HazeRegionStdDev { get; }
public string HazeAverageStdDev { get; }
//
public string WaferDate { get; }
public string Comments { get; }
public string Sort { get; }
public string WaferLPDCount { get; }
public string WaferLPDCM2 { get; }
public string Bin1 { get; }
public string Bin2 { get; }
public string Bin3 { get; }
public string Bin4 { get; }
public string Bin5 { get; }
public string Bin6 { get; }
public string Bin7 { get; }
public string Bin8 { get; }
public string Mean { get; }
public string StdDev { get; }
public string WaferAreaCount { get; }
public string WaferAreaTotal { get; }
public string WaferScratchCount { get; }
public string WaferScratchTotal { get; }
public string WaferSumOfDefects { get; }
public string WaferHazeRegion { get; }
public string WaferHazeAverage { get; }
public string HazePeak { get; }
public string Laser { get; }
public string Gain { get; }
public string Diameter { get; }
public string Thresh { get; }
public string Exclusion { get; }
public string HazeRng { get; }
public string Thruput { get; }
public string WaferRecipe { get; }
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Row))]
internal partial class RowSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,146 @@
using Adaptation.Shared;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Adaptation.FileHandlers.pdsf;
#nullable enable
internal class Run
{
public Header Header { get; }
public ReadOnlyCollection<Wafer> Wafers { get; }
public Run(Header header, ReadOnlyCollection<Wafer> wafers)
{
Header = header;
Wafers = wafers;
}
private static ReadOnlyCollection<Wafer> GetLastWaferForEachSlot(ReadOnlyDictionary<string, string> pages, Constant constant, string headerFileName, Header header)
{
List<Wafer> results = new();
string id;
Wafer wafer;
ReadOnlyCollection<Wafer>? wafers;
ReadOnlyDictionary<string, ReadOnlyCollection<Wafer>> keyValuePairs = Wafer.Get(pages, constant, headerFileName);
ReadOnlyCollection<string> waferIds = GetWaferIds(header);
for (int i = 0; i < waferIds.Count; i++)
{
id = waferIds[i];
if (!keyValuePairs.TryGetValue(id, out wafers) || wafers.Count == 0)
wafer = Wafer.Get(id);
else
wafer = (from l in wafers where l.Recipe == header.Recipe select l).Last();
if (wafer is null)
break;
results.Add(wafer);
}
return results.AsReadOnly();
}
private static void WriteJson(Logistics logistics, List<FileInfo> fileInfoCollection, Run result)
{
FileInfo fileInfo = new($"{logistics.ReportFullPath}.run.json");
string json = JsonSerializer.Serialize(result, RunSourceGenerationContext.Default.Run);
File.WriteAllText(fileInfo.FullName, json);
File.SetLastWriteTime(fileInfo.FullName, logistics.DateTimeFromSequence);
fileInfoCollection.Add(fileInfo);
}
private static ReadOnlyCollection<string> GetLines(Logistics logistics, JsonElement[]? jsonElements)
{
List<string> results = new();
int columns = 0;
StringBuilder stringBuilder = new();
results.Add($"\"Count\",{jsonElements?.Length}");
results.Add($"\"{nameof(logistics.Sequence)}\",\"{logistics.Sequence}\"");
results.Add($"\"{nameof(logistics.MesEntity)}\",\"{logistics.MesEntity}\"");
string dateTimeFromSequence = logistics.DateTimeFromSequence.ToString("MM/dd/yyyy hh:mm:ss tt");
for (int i = 0; i < jsonElements?.Length;)
{
_ = stringBuilder.Append('"').Append(nameof(logistics.DateTimeFromSequence)).Append('"').Append(',');
foreach (JsonProperty jsonProperty in jsonElements[0].EnumerateObject())
{
columns += 1;
_ = stringBuilder.Append('"').Append(jsonProperty.Name).Append('"').Append(',');
}
break;
}
if (jsonElements?.Length != 0)
_ = stringBuilder.Remove(stringBuilder.Length - 1, 1);
results.Add(stringBuilder.ToString());
for (int i = 0; i < jsonElements?.Length; i++)
{
_ = stringBuilder.Clear();
_ = stringBuilder.Append('"').Append(dateTimeFromSequence).Append('"').Append(',');
foreach (JsonProperty jsonProperty in jsonElements[i].EnumerateObject())
{
if (jsonProperty.Value.ValueKind == JsonValueKind.Object)
_ = stringBuilder.Append(',');
else if (jsonProperty.Value.ValueKind != JsonValueKind.String)
_ = stringBuilder.Append(jsonProperty.Value).Append(',');
else
_ = stringBuilder.Append('"').Append(jsonProperty.Value).Append('"').Append(',');
}
_ = stringBuilder.Remove(stringBuilder.Length - 1, 1);
results.Add(stringBuilder.ToString());
}
return results.AsReadOnly();
}
private static void WriteCommaSeparatedValues(Logistics logistics, Run run)
{
List<Row> results = new();
Row row;
for (int i = 0; i < run.Wafers.Count; i++)
{
row = new(run, i);
results.Add(row);
}
string json = JsonSerializer.Serialize(results);
JsonElement[]? jsonElements = JsonSerializer.Deserialize<JsonElement[]>(json);
ReadOnlyCollection<string> lines = GetLines(logistics, jsonElements);
File.WriteAllText($"{logistics.ReportFullPath}.csv", string.Join(Environment.NewLine, lines));
}
private static ReadOnlyCollection<string> GetWaferIds(Header header)
{
List<string> results = new();
foreach (WaferSummary waferSummary in header.WaferSummary)
results.Add(waferSummary.Id);
return results.AsReadOnly();
}
internal static Run? Get(Logistics logistics, List<FileInfo> fileInfoCollection, ReadOnlyDictionary<string, string> pages)
{
Run? result;
Constant constant = new();
string headerFileName = pages.ElementAt(pages.Count - 1).Key;
Header? header = Header.Get(pages, constant, headerFileName);
if (header is null)
result = null;
else
{
ReadOnlyCollection<Wafer> wafers = GetLastWaferForEachSlot(pages, constant, headerFileName, header);
result = new(header, wafers);
WriteJson(logistics, fileInfoCollection, result);
WriteCommaSeparatedValues(logistics, result);
}
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Run))]
internal partial class RunSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text.Json.Serialization;
namespace Adaptation.FileHandlers.pdsf;
#nullable enable
public class Wafer
{
public Wafer(string date, string id, string comments, string sort, string lPDCount, string lPDCM2, string bin1, string bin2, string bin3, string bin4, string bin5, string bin6, string bin7, string bin8, string mean, string stdDev, string areaCount, string areaTotal, string scratchCount, string scratchTotal, string sumOfDefects, string hazeRegion, string hazeAverage, string hazePeak, string laser, string gain, string diameter, string thresh, string exclusion, string hazeRng, string thruput, string recipe)
{
Date = date;
Id = id;
Comments = comments;
Sort = sort;
LPDCount = lPDCount;
LPDCM2 = lPDCM2;
Bin1 = bin1;
Bin2 = bin2;
Bin3 = bin3;
Bin4 = bin4;
Bin5 = bin5;
Bin6 = bin6;
Bin7 = bin7;
Bin8 = bin8;
Mean = mean;
StdDev = stdDev;
AreaCount = areaCount;
AreaTotal = areaTotal;
ScratchCount = scratchCount;
ScratchTotal = scratchTotal;
SumOfDefects = sumOfDefects;
HazeRegion = hazeRegion;
HazeAverage = hazeAverage;
HazePeak = hazePeak;
Laser = laser;
Gain = gain;
Diameter = diameter;
Thresh = thresh;
Exclusion = exclusion;
HazeRng = hazeRng;
Thruput = thruput;
Recipe = recipe;
}
internal static Wafer Get(string id) =>
new(date: string.Empty,
id: id,
comments: string.Empty,
sort: string.Empty,
lPDCount: string.Empty,
lPDCM2: string.Empty,
bin1: string.Empty,
bin2: string.Empty,
bin3: string.Empty,
bin4: string.Empty,
bin5: string.Empty,
bin6: string.Empty,
bin7: string.Empty,
bin8: string.Empty,
mean: string.Empty,
stdDev: string.Empty,
areaCount: string.Empty,
areaTotal: string.Empty,
scratchCount: string.Empty,
scratchTotal: string.Empty,
sumOfDefects: string.Empty,
hazeRegion: string.Empty,
hazeAverage: string.Empty,
hazePeak: string.Empty,
laser: string.Empty,
gain: string.Empty,
diameter: string.Empty,
thresh: string.Empty,
exclusion: string.Empty,
hazeRng: string.Empty,
thruput: string.Empty,
recipe: string.Empty);
public string Date { get; }
public string Id { get; }
public string Comments { get; }
public string Sort { get; }
public string LPDCount { get; }
public string LPDCM2 { get; }
public string Bin1 { get; }
public string Bin2 { get; }
public string Bin3 { get; }
public string Bin4 { get; }
public string Bin5 { get; }
public string Bin6 { get; }
public string Bin7 { get; }
public string Bin8 { get; }
public string Mean { get; }
public string StdDev { get; }
public string AreaCount { get; }
public string AreaTotal { get; }
public string ScratchCount { get; }
public string ScratchTotal { get; }
public string SumOfDefects { get; }
public string HazeRegion { get; }
public string HazeAverage { get; }
public string HazePeak { get; }
public string Laser { get; }
public string Gain { get; }
public string Diameter { get; }
public string Thresh { get; }
public string Exclusion { get; }
public string HazeRng { get; }
public string Thruput { get; }
public string Recipe { get; }
internal static ReadOnlyDictionary<string, ReadOnlyCollection<Wafer>> Get(ReadOnlyDictionary<string, string> pages, Constant constant, string headerFileName)
{
Dictionary<string, ReadOnlyCollection<Wafer>> results = new();
Wafer wafer;
string? text;
List<string> stringList;
int[] i = new int[] { 0 };
Dictionary<string, List<Wafer>> keyValuePairs = new();
foreach (KeyValuePair<string, string> keyValuePair in pages)
{
if (keyValuePair.Key == headerFileName)
continue;
if (!pages.ContainsKey(keyValuePair.Key))
throw new Exception();
i[0] = 0;
stringList = new();
if (!pages.TryGetValue(keyValuePair.Key, out text))
throw new Exception();
if (string.IsNullOrEmpty(text) || !text.Contains(constant.Id))
continue;
Header.ScanPast(text, i, constant.Date);
string date = Header.GetToEOL(text, i);
Header.ScanPast(text, i, constant.Id);
string id = Header.GetToEOL(text, i);
if (id.Length > 5)
id = string.Concat(id.Substring(0, 5), "... - ***");
id = id.Replace("*", "");
Header.ScanPast(text, i, "Comments:");
string comments = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Sort:");
string sort = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "LPD Count:");
string lPDCount = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "LPD / cm2:");
string lPDCM2 = Header.GetToEOL(text, i);
while (Header.GetBefore(text, i, ":").Contains("Bin"))
stringList.Add(Header.GetToEOL(text, i));
string bin1 = stringList.Count >= 1 ? stringList[0] : string.Empty;
string bin2 = stringList.Count >= 2 ? stringList[1] : string.Empty;
string bin3 = stringList.Count >= 3 ? stringList[2] : string.Empty;
string bin4 = stringList.Count >= 4 ? stringList[3] : string.Empty;
string bin5 = stringList.Count >= 5 ? stringList[4] : string.Empty;
string bin6 = stringList.Count >= 6 ? stringList[5] : string.Empty;
string bin7 = stringList.Count >= 7 ? stringList[6] : string.Empty;
string bin8 = stringList.Count >= 8 ? stringList[7] : string.Empty;
string mean = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Std Dev:");
string stdDev = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Area Count:");
string areaCount = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Area Total:");
string areaTotal = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Scratch Count:");
string scratchCount = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Scratch Total:");
string scratchTotal = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Sum of All Defects:");
string sumOfDefects = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Haze Region:");
string hazeRegion = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Haze Average:");
string hazeAverage = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Haze Peak:");
string hazePeak = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Laser:");
string laser = Header.GetBefore(text, i, "Gain:");
string gain = Header.GetBefore(text, i, "Diameter:");
string diameter = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Thresh:");
string thresh = Header.GetBefore(text, i, "Exclusion:");
string exclusion = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Haze Rng:");
string hazeRng = Header.GetBefore(text, i, "Thruput:");
string thruput = Header.GetToEOL(text, i);
Header.ScanPast(text, i, "Recipe ID:");
string recipe = Header.GetToEOL(text, i);
wafer = new(date: date,
id: id,
comments: comments,
sort: sort,
lPDCount: lPDCount,
lPDCM2: lPDCM2,
bin1: bin1,
bin2: bin2,
bin3: bin3,
bin4: bin4,
bin5: bin5,
bin6: bin6,
bin7: bin7,
bin8: bin8,
mean: mean,
stdDev: stdDev,
areaCount: areaCount,
areaTotal: areaTotal,
scratchCount: scratchCount,
scratchTotal: scratchTotal,
sumOfDefects: sumOfDefects,
hazeRegion: hazeRegion,
hazeAverage: hazeAverage,
hazePeak: hazePeak,
laser: laser,
gain: gain,
diameter: diameter,
thresh: thresh,
exclusion: exclusion,
hazeRng: hazeRng,
thruput: thruput,
recipe: recipe);
if (!keyValuePairs.ContainsKey(id))
keyValuePairs.Add(id, new List<Wafer>());
keyValuePairs[id].Add(wafer);
}
foreach (KeyValuePair<string, List<Wafer>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
return new(results);
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Wafer))]
internal partial class WaferSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,43 @@
using System.Text.Json.Serialization;
namespace Adaptation.FileHandlers.pdsf;
#nullable enable
public class WaferSummary
{
public WaferSummary(string id, string lPDCount, string lPDCM2, string areaCount, string areaTotal, string scratchCount, string scratchTotal, string sumOfDefects, string hazeRegion, string hazeAverage, string grade)
{
Id = id;
LPDCount = lPDCount;
LPDCM2 = lPDCM2;
AreaCount = areaCount;
AreaTotal = areaTotal;
ScratchCount = scratchCount;
ScratchTotal = scratchTotal;
SumOfDefects = sumOfDefects;
HazeRegion = hazeRegion;
HazeAverage = hazeAverage;
Grade = grade;
}
public string Id { get; }
public string LPDCount { get; }
public string LPDCM2 { get; }
public string AreaCount { get; }
public string AreaTotal { get; }
public string ScratchCount { get; }
public string ScratchTotal { get; }
public string SumOfDefects { get; }
public string HazeRegion { get; }
public string HazeAverage { get; }
public string Grade { get; }
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WaferSummary))]
internal partial class WaferSummarySourceGenerationContext : JsonSerializerContext
{
}