404 lines
16 KiB
C#
404 lines
16 KiB
C#
using Adaptation.Shared;
|
|
using Adaptation.Shared.Methods;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace Adaptation.FileHandlers.RsM;
|
|
|
|
public class ProcessData : IProcessData
|
|
{
|
|
|
|
private readonly List<object> _Details;
|
|
|
|
public string JobID { get; set; }
|
|
public string MesEntity { get; set; }
|
|
public string AutoOptimizeGain { get; set; }
|
|
public string AutoProbeHeightSet { get; set; }
|
|
public string Avg { get; set; }
|
|
public string DLRatio { get; set; }
|
|
public string DataReject { get; set; }
|
|
public DateTime Date { get; set; }
|
|
public string Employee { get; set; }
|
|
public string Engineer { get; set; }
|
|
public string EquipId { get; set; }
|
|
public string FileName { get; set; }
|
|
public string Layer { get; set; }
|
|
public string LogBody { get; set; }
|
|
public string Lot { get; set; }
|
|
public string PSN { get; set; }
|
|
public string Project { get; set; }
|
|
public string RDS { get; set; }
|
|
public string Reactor { get; set; }
|
|
public string Recipe { get; set; }
|
|
public string RecipeName { get; set; }
|
|
public string ResistivitySpec { get; set; }
|
|
public string Run { get; set; }
|
|
public string SemiRadial { get; set; }
|
|
public string StandardDeviation { get; set; }
|
|
public string StandardDeviationPercentage { get; set; }
|
|
public string Temp { get; set; }
|
|
public string Title { get; set; }
|
|
public string UniqueId { get; set; }
|
|
public string Zone { get; set; }
|
|
|
|
List<object> Shared.Properties.IProcessData.Details => _Details;
|
|
|
|
public ProcessData(IFileRead fileRead, Logistics logistics, List<FileInfo> fileInfoCollection)
|
|
{
|
|
JobID = logistics.JobID;
|
|
fileInfoCollection.Clear();
|
|
_Details = new List<object>();
|
|
MesEntity = logistics.MesEntity;
|
|
Parse(fileRead, logistics, fileInfoCollection);
|
|
}
|
|
|
|
string IProcessData.GetCurrentReactor(IFileRead fileRead, Logistics logistics, Dictionary<string, string> reactors) => throw new Exception(string.Concat("See ", nameof(Parse)));
|
|
|
|
Tuple<string, Test[], JsonElement[], List<FileInfo>> IProcessData.GetResults(IFileRead fileRead, Logistics logistics, List<FileInfo> fileInfoCollection)
|
|
{
|
|
Tuple<string, Test[], JsonElement[], List<FileInfo>> results;
|
|
List<Test> tests = new();
|
|
foreach (object item in _Details)
|
|
tests.Add(Test.CDE);
|
|
List<IDescription> descriptions = fileRead.GetDescriptions(fileRead, tests, this);
|
|
if (tests.Count != descriptions.Count)
|
|
throw new Exception();
|
|
for (int i = 0; i < tests.Count; i++)
|
|
{
|
|
if (descriptions[i] is not Description description)
|
|
throw new Exception();
|
|
if (description.Test != (int)tests[i])
|
|
throw new Exception();
|
|
}
|
|
List<Description> fileReadDescriptions = (from l in descriptions select (Description)l).ToList();
|
|
string json = JsonSerializer.Serialize(fileReadDescriptions, fileReadDescriptions.GetType());
|
|
JsonElement[] jsonElements = JsonSerializer.Deserialize<JsonElement[]>(json);
|
|
results = new Tuple<string, Test[], JsonElement[], List<FileInfo>>(logistics.Logistics1[0], tests.ToArray(), jsonElements, fileInfoCollection);
|
|
return results;
|
|
}
|
|
|
|
private static (string, string) GetReactorAndRDS(string defaultReactor, string defaultRDS, string text, string formattedText, string[] segments)
|
|
{
|
|
string rds;
|
|
string reactor;
|
|
if (string.IsNullOrEmpty(text) || segments.Length == 0 || string.IsNullOrEmpty(formattedText))
|
|
reactor = defaultReactor;
|
|
else
|
|
reactor = segments[0];
|
|
if (segments.Length <= 1 || !int.TryParse(segments[1], out int rdsValue) || rdsValue < 99)
|
|
rds = defaultRDS;
|
|
else
|
|
rds = segments[1];
|
|
if (reactor.Length > 3)
|
|
{
|
|
rds = reactor;
|
|
reactor = defaultReactor;
|
|
}
|
|
return new(reactor, rds);
|
|
}
|
|
|
|
private static (string, string) GetLayerAndPSN(string defaultLayer, string defaultPSN, string[] segments)
|
|
{
|
|
string psn;
|
|
string layer;
|
|
if (segments.Length <= 2)
|
|
{
|
|
psn = defaultPSN;
|
|
layer = defaultLayer;
|
|
}
|
|
else
|
|
{
|
|
string[] segmentsB = segments[2].Split('.');
|
|
psn = segmentsB[0];
|
|
if (segmentsB.Length <= 1)
|
|
layer = defaultLayer;
|
|
else
|
|
{
|
|
layer = segmentsB[1];
|
|
if (layer.Length > 1 && layer[0] == '0')
|
|
layer = layer.Substring(1);
|
|
}
|
|
}
|
|
return (layer, psn);
|
|
}
|
|
|
|
private static string GetZone(string[] segments)
|
|
{
|
|
string result;
|
|
if (segments.Length <= 3)
|
|
result = string.Empty;
|
|
else
|
|
{
|
|
result = segments[3];
|
|
if (result.Length > 1 && result[0] == '0')
|
|
result = result.Substring(1);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static Descriptor GetDescriptor(string text)
|
|
{
|
|
Descriptor result;
|
|
string psn;
|
|
string rds;
|
|
string run;
|
|
string zone;
|
|
string layer;
|
|
string title;
|
|
string reactor;
|
|
string employee;
|
|
string defaultPSN = string.Empty;
|
|
string defaultRDS = string.Empty;
|
|
string defaultZone = string.Empty;
|
|
string defaultLayer = string.Empty;
|
|
string defaultReactor = string.Empty;
|
|
string defaultEmployee = string.Empty;
|
|
if (Regex.IsMatch(text, @"^[a-zA-z][0-9]{2,4}$"))
|
|
{
|
|
run = text.ToUpper();
|
|
psn = defaultPSN;
|
|
rds = defaultRDS;
|
|
zone = defaultZone;
|
|
layer = defaultLayer;
|
|
reactor = defaultReactor;
|
|
employee = defaultEmployee;
|
|
}
|
|
else if (string.IsNullOrEmpty(text) || (text.Length is 2 or 3 && Regex.IsMatch(text, "^[a-zA-z]{2,3}")))
|
|
{
|
|
run = text;
|
|
employee = text;
|
|
psn = defaultPSN;
|
|
rds = defaultRDS;
|
|
zone = defaultZone;
|
|
layer = defaultLayer;
|
|
reactor = defaultReactor;
|
|
}
|
|
else if (Regex.IsMatch(text, @"^[0-9]{2}[.][0-9]{1}[.]?[0-9]{0,1}"))
|
|
{
|
|
string[] segments = text.Split('.');
|
|
run = text;
|
|
psn = defaultPSN;
|
|
rds = defaultRDS;
|
|
layer = segments[1];
|
|
reactor = segments[0];
|
|
employee = defaultEmployee;
|
|
if (segments.Length <= 2)
|
|
zone = defaultZone;
|
|
else
|
|
zone = segments[2];
|
|
}
|
|
else
|
|
{
|
|
// Remove illegal characters \/:*?"<>| found in the title.
|
|
title = Regex.Replace(text, @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]", "_").Split('\r')[0].Split('\n')[0];
|
|
if (title.Length > 2 && title[0] == '1' && (title[1] == 'T' || title[1] == 't'))
|
|
title = title.Substring(2);
|
|
run = title;
|
|
string[] segments = title.Split('-');
|
|
// bool hasRDS = Regex.IsMatch(title, "[-]?([QP][0-9]{4,}|[0-9]{5,})[-]?");
|
|
(reactor, rds) = GetReactorAndRDS(defaultReactor, defaultRDS, text, title, segments);
|
|
(layer, psn) = GetLayerAndPSN(defaultLayer, defaultPSN, segments);
|
|
zone = GetZone(segments);
|
|
employee = defaultEmployee;
|
|
}
|
|
result = new(employee, layer, psn, rds, reactor, run, zone);
|
|
return result;
|
|
}
|
|
|
|
private void SetTitleData(Logistics logistics, string text)
|
|
{
|
|
string timeFormat = "yyyyMMddHHmmss";
|
|
Descriptor descriptor = GetDescriptor(text);
|
|
PSN = descriptor.PSN;
|
|
RDS = descriptor.RDS;
|
|
Run = descriptor.Run;
|
|
Zone = descriptor.Zone;
|
|
Layer = descriptor.Layer;
|
|
Reactor = descriptor.Reactor;
|
|
Employee = descriptor.Employee;
|
|
UniqueId = string.Concat(logistics.JobID, "_", descriptor.Run, "_", logistics.DateTimeFromSequence.ToString(timeFormat));
|
|
}
|
|
|
|
private void SetFileNameData(string[] segments)
|
|
{
|
|
if (segments.Length > 1)
|
|
FileName = segments[0];
|
|
if (segments.Length > 2)
|
|
{
|
|
Project = segments[1];
|
|
RecipeName = segments[2];
|
|
Recipe = string.Concat(segments[1], " \\ ", segments[2]);
|
|
}
|
|
}
|
|
|
|
internal static DateTime GetDateTime(Logistics logistics, string dateTimeText)
|
|
{
|
|
DateTime result;
|
|
string inputDateFormat = "HH:mm MM/dd/yy";
|
|
if (dateTimeText.Length != inputDateFormat.Length)
|
|
result = logistics.DateTimeFromSequence;
|
|
else
|
|
{
|
|
if (!DateTime.TryParseExact(dateTimeText, inputDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateTimeParsed))
|
|
result = logistics.DateTimeFromSequence;
|
|
else
|
|
{
|
|
if (dateTimeParsed < logistics.DateTimeFromSequence.AddDays(1) && dateTimeParsed > logistics.DateTimeFromSequence.AddDays(-1))
|
|
result = dateTimeParsed;
|
|
else
|
|
result = logistics.DateTimeFromSequence;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private void SetDateTimeData(Logistics logistics, string[] segments)
|
|
{
|
|
DateTime dateTime;
|
|
if (segments.Length < 2)
|
|
dateTime = logistics.DateTimeFromSequence;
|
|
else
|
|
{
|
|
string dateTimeText = string.Concat(segments[0], ' ', segments[1]);
|
|
dateTime = GetDateTime(logistics, dateTimeText);
|
|
}
|
|
Date = dateTime;
|
|
if (segments.Length > 3 && float.TryParse(segments[2], out float temp))
|
|
Temp = temp.ToString("0.0");
|
|
if (segments.Length > 7 && segments[6] == "Avg=")
|
|
Avg = segments[7];
|
|
if (segments.Length > 7 && segments[8] == "Dev=")
|
|
StandardDeviation = segments[9];
|
|
if (!string.IsNullOrEmpty(Avg) && !string.IsNullOrEmpty(StandardDeviation) && float.TryParse(Avg, out float average) && float.TryParse(StandardDeviation, out float standardDeviation))
|
|
StandardDeviationPercentage = Math.Round(standardDeviation / average, 4).ToString("0.00%");
|
|
}
|
|
|
|
private void SetOperatorData(string[] segments, bool updateEmployee)
|
|
{
|
|
if (segments.Length > 1 && updateEmployee)
|
|
Employee = segments[0];
|
|
if (segments.Length > 2)
|
|
EquipId = segments[1];
|
|
}
|
|
|
|
private void SetEngineerData(string[] segments)
|
|
{
|
|
if (segments.Length > 1)
|
|
Engineer = segments[0];
|
|
}
|
|
|
|
private void SetNumProbePointsData(string[] segments)
|
|
{
|
|
if (segments.Length > 6)
|
|
DataReject = segments[6];
|
|
}
|
|
|
|
private static Detail GetRData(string[] segments)
|
|
{
|
|
Detail result = new();
|
|
if (segments.Length > 0 && float.TryParse(segments[0], out float r))
|
|
result.R = r.ToString("0.0");
|
|
if (segments.Length > 1 && float.TryParse(segments[1], out float t))
|
|
result.T = t.ToString("0.0");
|
|
if (segments.Length > 2 && float.TryParse(segments[2], out float rs))
|
|
result.Rs = rs.ToString("0.0000");
|
|
if (segments.Length > 12 && float.TryParse(segments[12], out float merit))
|
|
result.Merit = merit.ToString("0.00");
|
|
result.Pt = "-1";
|
|
result.UniqueId = string.Empty;
|
|
return result;
|
|
}
|
|
|
|
#pragma warning disable IDE0060
|
|
private void Parse(IFileRead fileRead, Logistics logistics, List<FileInfo> fileInfoCollection)
|
|
#pragma warning restore IDE0060
|
|
{
|
|
Lot = "LotID";
|
|
Detail detail;
|
|
string[] segments;
|
|
string[] separator = new string[] { " " };
|
|
string[] lines = File.ReadAllLines(logistics.ReportFullPath);
|
|
for (int i = 0; i < lines.Length; i++)
|
|
{
|
|
segments = lines[i].Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
|
if (lines[i].Contains("<Title>") && segments.Length > 0)
|
|
SetTitleData(logistics, segments[0]);
|
|
else if (lines[i].Contains("<FileName, Proj,Rcpe, LotID,WfrID"))
|
|
SetFileNameData(segments);
|
|
else if (lines[i].Contains("<DateTime,Temp,TCR%,N|P>"))
|
|
SetDateTimeData(logistics, segments);
|
|
else if (lines[i].Contains("<Operator, Epuipment>"))
|
|
SetOperatorData(segments, updateEmployee: string.IsNullOrEmpty(Employee));
|
|
else if (lines[i].Contains("<Engineer>"))
|
|
SetEngineerData(segments);
|
|
else if (lines[i].Contains("<NumProbePoints, SingleOrDualProbeConfig, #ActPrbPts, Rsens,IdrvMx,VinGain, DataRejectSigma, MeritThreshold"))
|
|
SetNumProbePointsData(segments);
|
|
else if (lines[i].Contains("<R,Th,Data, Rs,RsA,RsB, #Smpl, x,y, Irng,Vrng"))
|
|
{
|
|
for (int z = i; z < lines.Length; z++)
|
|
{
|
|
i = z;
|
|
if (string.IsNullOrEmpty(lines[z]))
|
|
continue;
|
|
detail = GetRData(lines[z].Split(separator, StringSplitOptions.RemoveEmptyEntries));
|
|
_Details.Add(detail);
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < _Details.Count; i++)
|
|
{
|
|
if (_Details[i] is not Detail item)
|
|
continue;
|
|
item.HeaderUniqueId = UniqueId;
|
|
item.Pt = (i + 1).ToString();
|
|
item.UniqueId = string.Concat(item, "_Point-", item.Pt);
|
|
}
|
|
StringBuilder stringBuilder = new();
|
|
string reportFileName = Path.GetFileName(logistics.ReportFullPath);
|
|
_ = stringBuilder.AppendLine($"RUN [{Title}]");
|
|
_ = stringBuilder.AppendLine($"Recipe {Project} \\ {RecipeName} RESISTIVITY {"####"}");
|
|
_ = stringBuilder.AppendLine($"EQUIP# {EquipId} Engineer {Engineer}");
|
|
_ = stringBuilder.AppendLine($"LotID {Lot} D.L.RATIO {"#.####"}");
|
|
_ = stringBuilder.AppendLine($"OPERATOR {Employee} TEMP {Temp} {Date:HH:mm MM/dd/yy}");
|
|
_ = stringBuilder.AppendLine($"AutoOptimizeGain = {"###"} AutoProbeHeightSet = {"##"}");
|
|
_ = stringBuilder.AppendLine($"DataReject > {"#.#"}Sigma");
|
|
_ = stringBuilder.AppendLine($"0 ..\\{Project}.prj\\{RecipeName}.rcp\\{reportFileName} {Date:HH:mm MM/dd/yy}");
|
|
_ = stringBuilder.AppendLine($"pt# R Th Rs[Ohm/sq@T] Merit");
|
|
for (int i = 0; i < _Details.Count; i++)
|
|
{
|
|
if (_Details[i] is not Detail item)
|
|
continue;
|
|
_ = stringBuilder.AppendLine($"{item.Pt} {item.R} {item.T} {item.Rs} {item.Merit}");
|
|
}
|
|
_ = stringBuilder.AppendLine($"Avg = {Avg} {StandardDeviationPercentage} SEMI Radial= {"#.##%"}");
|
|
LogBody = stringBuilder.ToString();
|
|
}
|
|
|
|
#nullable enable
|
|
|
|
internal static List<Description> GetDescriptions(JsonElement[] jsonElements)
|
|
{
|
|
List<Description> results = new();
|
|
Description? description;
|
|
JsonSerializerOptions jsonSerializerOptions = new() { NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString };
|
|
foreach (JsonElement jsonElement in jsonElements)
|
|
{
|
|
if (jsonElement.ValueKind != JsonValueKind.Object)
|
|
throw new Exception();
|
|
description = JsonSerializer.Deserialize<Description>(jsonElement.ToString(), jsonSerializerOptions);
|
|
if (description is null)
|
|
continue;
|
|
results.Add(description);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
} |