using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.IO; using System.Linq; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; namespace Adaptation.FileHandlers.TIBCO.Transport; public class Job { #nullable restore public string AutomationMode { get; } public string BasicType { get; } public string CreationUser { get; } public string Equipment { get; } public string JobName { get; } public string LastUpdateUser { get; } public string LotName { get; } public string LotState { get; } public string PackageName { get; } public string ProcessSpecName { get; } public string ProcessType { get; } public string ProductName { get; } public string Qty { get; } public string Qty2 { get; } public string RecipeName { get; } public string SpecName { get; } public string StateModel { get; } public string Status { get; } // public bool IsAreaSi { get; } public DateTime DateTime { get; } public List Items { get; } public Job(string lsl2SQLConnectionString, string metrologyFileShare, string mid) { const int zero = 0; Items = new List(); if (string.IsNullOrEmpty(mid) || mid[zero] != '{' || mid[mid.Length - 1] != '}' || !mid.Contains("\"Si\"")) IsAreaSi = false; else { string psn; string zone; string layer; int? rdsNumber; string comment; const string hyphen = "-"; Input input = JsonSerializer.Deserialize(mid); if (!long.TryParse(input.Sequence, out long sequence)) DateTime = DateTime.Now; else DateTime = new DateTime(sequence); int? reactorNumber = GetReactorNumber(input); (int? workOrderNumber, int? _, int? workOrderCassette, int? slotNumber, bool isWorkOrder) = GetWorkOrder(input); if (isWorkOrder || reactorNumber.HasValue) (layer, psn, rdsNumber, zone) = (string.Empty, string.Empty, null, string.Empty); else if (!string.IsNullOrEmpty(input.MID) && input.MID.Length is not 2 and not 3) (layer, psn, rdsNumber, reactorNumber, zone) = Get(input); else (layer, psn, rdsNumber, reactorNumber, zone) = Get(metrologyFileShare, input); if (IsValid(rdsNumber)) (comment, layer, rdsNumber, psn, reactorNumber, zone) = GetWithValidRDS(lsl2SQLConnectionString, layer, psn, rdsNumber, reactorNumber, zone); else if (isWorkOrder || reactorNumber.HasValue) (comment, layer, rdsNumber, psn, reactorNumber, zone) = Get(lsl2SQLConnectionString, layer, psn, reactorNumber, slotNumber, workOrderNumber, workOrderCassette, zone); else (comment, layer, zone) = (hyphen, hyphen, hyphen); Qty = "1"; Status = hyphen; // INFO CreationUser = hyphen; // ? LotState = hyphen; // LAYER2 LastUpdateUser = hyphen; // ? Equipment = input.MesEntity; // ? PackageName = hyphen; // WAFER_ID Qty2 = input.Sequence; // SEQUENCE RecipeName = input.Recipe; // PPID IsAreaSi = input.Area == "Si"; // N/A StateModel = input.EquipmentType; // ? JobName = DateTime.Ticks.ToString(); // ? SpecName = !string.IsNullOrEmpty(layer) ? layer : hyphen; // LAYER ProductName = !string.IsNullOrEmpty(psn) ? psn : hyphen; // PRODUCT AutomationMode = string.Concat(DateTime.Ticks, ".", input.MesEntity); // ? ProcessSpecName = !string.IsNullOrEmpty(zone) ? zone : hyphen; // WAFER_POS BasicType = !string.IsNullOrEmpty(comment) ? comment : hyphen; // BASIC_TYPE LotName = rdsNumber is not null ? rdsNumber.Value.ToString() : hyphen; // MID ProcessType = reactorNumber is not null ? reactorNumber.Value.ToString() : hyphen; // PROCESS_JOBID Items.Add(new Item { Name = "0", Type = "NA", Number = (0 + 1).ToString(), Qty = "1", CarrierName = hyphen }); } } private static int? GetReactorNumber(Input input) { int? result; bool isReactor = !string.IsNullOrEmpty(input.MID) && input.MID.Length == 2 && Regex.IsMatch(input.MID, "^[0-9]{2}$"); if (!isReactor) result = null; else if (!int.TryParse(input.MID, out int reactor)) result = null; else result = reactor; return result; } private static (int?, int?, int?, int?, bool) GetWorkOrder(Input input) { int? slotNumber; int? workOrderStep = null; int? workOrderNumber = null; MatchCollection[] collection; int? workOrderCassette = null; if (string.IsNullOrEmpty(input.MID)) collection = Array.Empty(); else { string pattern = @"^([oiOI])?([0-9]{6,7})\.([0-5]{1})\.([0-9]{1,2})$"; // o171308.1.51 collection = (from l in input.MID.Split('-') select Regex.Matches(l, pattern)).ToArray(); } foreach (MatchCollection matchCollection in collection) { if (matchCollection.Count == 0) continue; if (!matchCollection[0].Success || matchCollection[0].Groups.Count != 5) continue; if (!int.TryParse(matchCollection[0].Groups[3].Value, out int workOrderStepValue)) continue; if (!int.TryParse(matchCollection[0].Groups[2].Value, out int workOrderNumberValue)) continue; if (!int.TryParse(matchCollection[0].Groups[4].Value, out int workOrderCassetteValue)) continue; workOrderStep = workOrderStepValue; workOrderNumber = workOrderNumberValue; workOrderCassette = workOrderCassetteValue; break; } if (string.IsNullOrEmpty(input.Slot) || !int.TryParse(input.Slot, out int slot)) slotNumber = null; else slotNumber = slot; return new(workOrderNumber, workOrderStep, workOrderCassette, slotNumber, workOrderStep is not null || workOrderNumber is not null || workOrderCassette is not null); } private static bool IsValid(int? rdsNumber) => !IsInvalid(rdsNumber); private static bool IsInvalid(int? rdsNumber) => rdsNumber is null or < 100000 or > 100000000; 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; } private static (string, string, int, int?, string) Get(Input input) { string psn; string rds; string zone; string layer; int rdsNumber; string reactor; int? reactorNumber; string defaultPSN = string.Empty; string defaultRDS = string.Empty; string defaultLayer = string.Empty; string defaultReactor = string.Empty; string[] segments = input.MID.Split(new char[] { '-' }); string formattedText = Regex.Replace(input.MID, @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]", "_").Split('\r')[0].Split('\n')[0]; (reactor, rds) = GetReactorAndRDS(defaultReactor, defaultRDS, input.MID, formattedText, segments); if (string.IsNullOrEmpty(rds)) rdsNumber = 0; else _ = int.TryParse(segments[1], out rdsNumber); if (IsInvalid(rdsNumber) || !int.TryParse(reactor, out int reactorCheck)) { psn = string.Empty; zone = string.Empty; layer = string.Empty; reactorNumber = null; } else { reactorNumber = reactorCheck; (layer, psn) = GetLayerAndPSN(defaultLayer, defaultPSN, segments); zone = GetZone(segments); } return new(layer, psn, rdsNumber, reactorNumber, zone); } private static (string, string, int?, int?, string) Get(string metrologyFileShare, Input input) { string[] files; string[] lines; string moveFile; int? reactor = null; int? rdsNumber = null; string psn = string.Empty; string zone = string.Empty; string layer = string.Empty; string usedDirectory = Path.Combine(metrologyFileShare, "Used"); if (!Directory.Exists(metrologyFileShare)) throw new Exception($"Unable to access file-share <{metrologyFileShare}>"); if (!Directory.Exists(usedDirectory)) _ = Directory.CreateDirectory(usedDirectory); if (string.IsNullOrEmpty(input.MID)) files = Array.Empty(); else files = Directory.GetFiles(metrologyFileShare, $"*-{input.MID}.rsv", SearchOption.TopDirectoryOnly); foreach (string file in files.OrderByDescending(l => new FileInfo(l).LastWriteTime)) { lines = File.ReadAllLines(file); foreach (string line in lines) { if (string.IsNullOrEmpty(line)) continue; if (!int.TryParse(line, out int rds) || IsInvalid(rds)) continue; rdsNumber = rds; break; } if (rdsNumber is not null) { moveFile = Path.Combine(usedDirectory, Path.GetFileName(file)); if (File.Exists(moveFile)) File.Delete(file); else File.Move(file, moveFile); break; } } return new(layer, psn, rdsNumber, reactor, zone); } private static string GetRunJson(string lsl2SQLConnectionString, int? rds, int? workOrderNumber, int? workOrderCassette, int? slot, int? reactor) { StringBuilder result = new(); string commandText = GetCommandText(rds, workOrderNumber, workOrderCassette, slot, reactor); try { using SqlConnection sqlConnection = new(lsl2SQLConnectionString); sqlConnection.Open(); using SqlCommand sqlCommand = new(commandText, sqlConnection); SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(CommandBehavior.SequentialAccess); while (sqlDataReader.Read()) _ = result.Append(sqlDataReader.GetString(0)); } catch (Exception) { _ = result.Clear(); } return result.ToString(); } private static string GetCommandText(int? rds, int? workOrderNumber, int? workOrderCassette, int? slot, int? reactor) { StringBuilder result = new(); _ = result.Append(" select "). Append(" rr.rds_no "). Append(" , rr.reactor "). Append(" , rr.ps_no "). Append(" , rr.load_lock_side "). Append(" , rr.reactor_type "). Append(" , rr.recipe_name "). Append(" , rr.recipe_no "). Append(" , rr.spec_type "). Append(" , ( "). Append(" select max(wm.zone) "). Append(" from lsl2sql.dbo.wm_in_slot_no wm "). Append(" where wm.wo_no = rr.wo_no "). Append(" and wm.rds_no = rr.rds_no "). Append(" and wm.in_cass_no = ").Append(workOrderCassette is null ? -1 : workOrderCassette.Value).Append(' '). Append(" and wm.slot_no = ").Append(slot is null ? -1 : slot.Value).Append(' '). Append(" ) zone "). Append(" from lsl2sql.dbo.react_run rr "). Append(" where rr.rds_no = ").Append(rds is null ? -1 : rds.Value).Append(' '). Append(" union all "). Append(" select "). Append(" rr.rds_no "). Append(" , rr.reactor "). Append(" , rr.ps_no "). Append(" , rr.load_lock_side "). Append(" , rr.reactor_type "). Append(" , rr.recipe_name "). Append(" , rr.recipe_no "). Append(" , rr.spec_type "). Append(" , ( "). Append(" select max(wm.zone) "). Append(" from lsl2sql.dbo.wm_in_slot_no wm "). Append(" where wm.wo_no = rr.wo_no "). Append(" and wm.rds_no = rr.rds_no "). Append(" and wm.in_cass_no = ").Append(workOrderCassette is null ? -1 : workOrderCassette.Value).Append(' '). Append(" and wm.slot_no = ").Append(slot is null ? -1 : slot.Value).Append(' '). Append(" ) zone "). Append(" from lsl2sql.dbo.react_run rr "). Append(" where rr.rds_no = ( "). Append(" select max(wm.rds_no) "). Append(" from lsl2sql.dbo.wm_in_slot_no wm "). Append(" where wm.wo_no = ").Append(workOrderNumber is null ? -1 : workOrderNumber.Value).Append(' '). Append(" and wm.in_cass_no = ").Append(workOrderCassette is null ? -1 : workOrderCassette.Value).Append(' '). Append(" and wm.slot_no = ").Append(slot is null ? -1 : slot.Value).Append(' '). Append(" ) "). Append(" union all "). Append(" select "). Append(" rr.rds_no "). Append(" , rr.reactor "). Append(" , rr.ps_no "). Append(" , rr.load_lock_side "). Append(" , rr.reactor_type "). Append(" , rr.recipe_name "). Append(" , rr.recipe_no "). Append(" , rr.spec_type "). Append(" , ( "). Append(" select max(wm.zone) "). Append(" from lsl2sql.dbo.wm_in_slot_no wm "). Append(" where wm.wo_no = rr.wo_no "). Append(" and wm.rds_no = rr.rds_no "). Append(" and wm.in_cass_no = ").Append(workOrderCassette is null ? -1 : workOrderCassette.Value).Append(' '). Append(" and wm.slot_no = ").Append(slot is null ? -1 : slot.Value).Append(' '). Append(" ) zone "). Append(" from lsl2sql.dbo.react_run rr "). Append(" where rr.rds_no = ( "). Append(" select max(qa.rds_no) "). Append(" from lsl2sql.dbo.react_run qa "). Append(" where qa.load_sig != '' "). Append(" and qa.load_sig_dtm > '2022-07-01 00:00:00.000' "). Append(" and qa.reactor = ").Append(reactor is null ? -1 : reactor.Value).Append(' '). Append(" ) "). Append(" for json path "); return result.ToString(); } private static (string, string, int?, string, int?, string) Get(string lsl2SQLConnectionString, string layer, string psn, int? reactorNumber, int? slotNumber, int? workOrderNumber, int? workOrderCassette, string zone) { int? rdsNumber; string comment; const int zero = 0; const string hyphen = "-"; string json = GetRunJson(lsl2SQLConnectionString, rds: null, workOrderNumber, workOrderCassette, slotNumber, reactorNumber); if (string.IsNullOrEmpty(json)) { rdsNumber = null; comment = hyphen; psn = string.Empty; zone = string.Empty; } else { Run[] runs; try { runs = JsonSerializer.Deserialize(json); } catch (Exception) { runs = Array.Empty(); } if (!runs.Any()) { rdsNumber = null; comment = hyphen; psn = string.Empty; zone = string.Empty; } else { rdsNumber = runs[zero].RdsNo; if (string.IsNullOrEmpty(psn)) psn = runs[zero].PSN; if (string.IsNullOrEmpty(zone)) zone = runs[zero].Zone; if (string.IsNullOrEmpty(layer)) layer = runs[zero].EpiLayer; reactorNumber = runs[zero].Reactor; string loadLockSide = runs[zero].LoadLockSide; string loadLockSideFull = loadLockSide switch { "L" => "Left", "R" => "Right", _ => loadLockSide, }; comment = $"{loadLockSideFull} - {runs[zero].ReactorType}"; } } return new(comment, layer, rdsNumber, psn, reactorNumber, zone); } private static (string, string, int?, string, int?, string) GetWithValidRDS(string lsl2SQLConnectionString, string layer, string psn, int? rdsNumber, int? reactorNumber, string zone) { string comment; const int zero = 0; const string hyphen = "-"; string json = GetRunJson(lsl2SQLConnectionString, rdsNumber, workOrderNumber: null, workOrderCassette: null, slot: null, reactor: null); if (string.IsNullOrEmpty(json)) { comment = hyphen; zone = string.Empty; } else { Run[] runs; try { runs = JsonSerializer.Deserialize(json); } catch (Exception) { runs = Array.Empty(); } if (!runs.Any()) { comment = hyphen; zone = string.Empty; } else { if (string.IsNullOrEmpty(psn)) psn = runs[zero].PSN; if (string.IsNullOrEmpty(zone)) zone = runs[zero].Zone; if (string.IsNullOrEmpty(layer)) layer = runs[zero].EpiLayer; reactorNumber = runs[zero].Reactor; string loadLockSide = runs[zero].LoadLockSide; string loadLockSideFull = loadLockSide switch { "L" => "Left", "R" => "Right", _ => loadLockSide, }; comment = $"{loadLockSideFull} - {runs[zero].ReactorType}"; } } return new(comment, layer, rdsNumber, psn, reactorNumber, zone); } }