using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Adaptation.FileHandlers.TIBCO.Transport; #nullable enable public partial class Job { 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 barcodeHostFileShare, HttpClient httpClient, string mid, DateTime enteredDateTimeFilter, DateTime loadSignatureDateTimeFilter) { const int zero = 0; Items = new List(); if (string.IsNullOrEmpty(mid) || mid[zero] != '{' || mid[mid.Length - 1] != '}' || !mid.Contains("\"Si\"")) IsAreaSi = false; else { Common common; CommonB commonB; int? reactorNumber; const string hyphen = "-"; const string bioRad2 = "BIORAD2"; const string bioRad3 = "BIORAD3"; const string twoAlphaPattern = "^[a-zA-z]{2,3}"; const string reactorNumberPattern = @"^[0-9]{2}--"; Input input = JsonSerializer.Deserialize(mid) ?? throw new Exception(); if (!long.TryParse(input.Sequence, out long sequence)) DateTime = DateTime.Now; else DateTime = new DateTime(sequence); const string dep08CEPIEPSILON = "DEP08CEPIEPSILON"; if (input.EquipmentType == dep08CEPIEPSILON) { common = Get(input, httpClient); } else if (!string.IsNullOrEmpty(input.MID) && !string.IsNullOrEmpty(input.MesEntity) && Regex.IsMatch(input.MID, reactorNumberPattern) && input.MesEntity is bioRad2 or bioRad3) common = Get(input, barcodeHostFileShare); else { reactorNumber = GetReactorNumber(input); WorkOrder workOrder = GetWorkOrder(input); if (workOrder.IsWorkOrder || reactorNumber.HasValue) common = new(layer: null, psn: null, rdsNumber: null, reactor: null, zone: null, employee: null, workOrder: workOrder); else if (!string.IsNullOrEmpty(input.MID) && input.MID.Length is 2 or 3 && Regex.IsMatch(input.MID, twoAlphaPattern)) common = GetTwoAlphaPattern(metrologyFileShare, input); else common = Get(input); } bool isValid = IsValid(common.RDSNumber); if (isValid) commonB = GetWithValidRDS(lsl2SQLConnectionString, enteredDateTimeFilter, loadSignatureDateTimeFilter, common.Layer, common.PSN, common.RDSNumber, common.ReactorNumber, common.Zone); else if (common.WorkOrder is null || common.WorkOrder.IsWorkOrder || common.RDSNumber.HasValue) commonB = Get(lsl2SQLConnectionString, enteredDateTimeFilter, loadSignatureDateTimeFilter, common); else commonB = new(layer: hyphen, loadLockSide: hyphen, rdsNumber: common.RDSNumber, reactorType: hyphen, psn: common.PSN, reactorNumber: common.ReactorNumber, zone: 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(); // ? BasicType = GetComment(hyphen, httpClient, commonB); // BASIC_TYPE AutomationMode = string.Concat(DateTime.Ticks, ".", input.MesEntity); // ? SpecName = !string.IsNullOrEmpty(commonB.Layer) ? commonB.Layer : hyphen; // LAYER ProductName = !string.IsNullOrEmpty(commonB.PSN) ? commonB.PSN : hyphen; // PRODUCT ProcessSpecName = !string.IsNullOrEmpty(commonB.Zone) ? commonB.Zone : hyphen; // WAFER_POS LotName = commonB.RDSNumber is not null ? commonB.RDSNumber.Value.ToString() : input.MID; // MID ProcessType = commonB.ReactorNumber is not null ? commonB.ReactorNumber.Value.ToString() : hyphen; // PROCESS_JOBID Items.Add(new Item { Name = "0", Type = "NA", Number = (0 + 1).ToString(), Qty = "1", CarrierName = hyphen }); } } private static string GetComment(string hyphen, HttpClient httpClient, CommonB commonB) { string result; string? loadLockSide = commonB.LoadLockSide; if (string.IsNullOrEmpty(loadLockSide) && commonB.RDSNumber is not null) { RunDataSheetRoot? runDataSheetRoot; try { runDataSheetRoot = GetRunDataSheetRoot(httpClient, commonB.RDSNumber.Value); } catch (Exception) { runDataSheetRoot = null; } loadLockSide = runDataSheetRoot?.RunDataSheet?.LoadLockSide; } if (string.IsNullOrEmpty(loadLockSide) || string.IsNullOrEmpty(commonB.ReactorType)) result = hyphen; else { string loadLockSideFull = loadLockSide switch { "L" => "Left", "R" => "Right", _ => loadLockSide, }; result = $"{loadLockSideFull} - {commonB.ReactorType}"; } return result; } 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 WorkOrder GetWorkOrder(Input input) { WorkOrder result; 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; result = new(workOrderNumber: workOrderNumber, workOrderStep: workOrderStep, workOrderCassette: workOrderCassette, slotNumber: slotNumber, isWorkOrder: workOrderStep is not null || workOrderNumber is not null || workOrderCassette is not null); return result; } 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? text, string formattedText, string[] segments) { string? rds; string? reactor; if (string.IsNullOrEmpty(text) || segments.Length < 1 || string.IsNullOrEmpty(formattedText)) reactor = null; else reactor = segments[0]; if (segments.Length < 2 || !int.TryParse(segments[1], out int rdsValue) || rdsValue < 99) rds = null; else rds = segments[1]; if (!string.IsNullOrEmpty(reactor) && reactor.Length > 3) { rds = reactor; reactor = null; } return new(reactor, rds); } private static (string?, string?) GetLayerAndPSN(string[] segments) { string? psn; string? layer; if (segments.Length <= 2) { psn = null; layer = null; } else { string[] segmentsB = segments[2].Split('.'); psn = segmentsB[0]; if (segmentsB.Length <= 1) layer = null; 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 = null; else { result = segments[3]; if (result.Length > 1 && result[0] == '0') result = result.Substring(1); } return result; } private static Common Get(Input input) { string? psn; string? rds; int rdsCheck; string? zone; string? layer; int? rdsNumber; string? reactor; string? employee; int? reactorNumber; string mid = string.IsNullOrEmpty(input.MID) ? string.Empty : input.MID; if (mid.Length > 2 && mid[0] == '1' && (mid[1] == 'T' || mid[1] == 't')) mid = mid.Substring(2); mid = Regex.Replace(mid, @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]", "_").Split('\r')[0].Split('\n')[0]; string[] segments = mid.Split(new char[] { '-' }); // bool hasRDS = Regex.IsMatch(mid, "[-]?([QP][0-9]{4,}|[0-9]{5,})[-]?"); (reactor, rds) = GetReactorAndRDS(input.MID, mid, segments); rdsNumber = string.IsNullOrEmpty(rds) || !int.TryParse(rds, out rdsCheck) ? null : rdsCheck; bool isInvalid = IsInvalid(rdsNumber); if (isInvalid || !int.TryParse(reactor, out int reactorCheck)) { psn = null; zone = null; layer = null; employee = null; reactorNumber = null; } else { zone = GetZone(segments); reactorNumber = reactorCheck; (layer, psn) = GetLayerAndPSN(segments); employee = segments.Length <= 4 ? null : segments[4]; } return new(layer: layer, psn: psn, rdsNumber: rdsNumber, reactor: reactorNumber, zone: zone, employee: employee, workOrder: null); } private static string[] GetDirectories(string fileShare) { DateTime dateTime = DateTime.Now; DateTime before = dateTime.AddHours(-1); Calendar calendar = new CultureInfo("en-US").Calendar; string weekOfYear = $"{dateTime:yyyy}_Week_{calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}"; string weekOfYearForBefore = $"{before:yyyy}_Week_{calendar.GetWeekOfYear(before, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}"; return new string[] { Path.Combine(fileShare, weekOfYear, dateTime.ToString("yyyy-MM-dd_HH")), Path.Combine(fileShare, weekOfYearForBefore, before.ToString("yyyy-MM-dd_HH")) }; } private static Common GetTwoAlphaPattern(string metrologyFileShare, Input input) { string lines; const int zero = 0; string? psn = null; int? reactor = null; string? zone = null; string? layer = null; int? rdsNumber = null; List files = new(); WorkMaterialOut? workMaterialOut; if (string.IsNullOrEmpty(metrologyFileShare) || !Directory.Exists(metrologyFileShare)) throw new Exception($"Unable to access file-share <{metrologyFileShare}>"); if (!string.IsNullOrEmpty(input.MID) && input.MID.Length is 3) { string[] checkDirectories = GetDirectories(metrologyFileShare); foreach (string checkDirectory in checkDirectories) { if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); files.AddRange(Directory.GetFiles(checkDirectory, $"WMO-{input.MID}.json", SearchOption.TopDirectoryOnly)); files.AddRange(Directory.GetFiles(checkDirectory, $"WMO-{input.MID.Substring(1, 2)}{input.MID[zero]}.json", SearchOption.TopDirectoryOnly)); } } foreach (string file in files.OrderByDescending(l => new FileInfo(l).LastWriteTime)) { lines = File.ReadAllText(file); workMaterialOut = JsonSerializer.Deserialize(lines, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); if (workMaterialOut is null) continue; if (!string.IsNullOrEmpty(workMaterialOut.Layer)) layer = workMaterialOut.Layer; if (!string.IsNullOrEmpty(workMaterialOut.PSN)) psn = workMaterialOut.PSN; if (!string.IsNullOrEmpty(workMaterialOut.RunDataSheet) && int.TryParse(workMaterialOut.RunDataSheet, out int rds)) rdsNumber = rds; reactor = workMaterialOut.Reactor; if (!string.IsNullOrEmpty(workMaterialOut.Zone)) zone = workMaterialOut.Zone; break; } return new(layer: layer, psn: psn, rdsNumber: rdsNumber, reactor: reactor, zone: zone, employee: null, workOrder: null); } private static List GetFiles(Input input, string barcodeHostFileShare) { List results = new(); string[] checkDirectories = GetDirectories(barcodeHostFileShare); foreach (string checkDirectory in checkDirectories) { if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); results.AddRange(Directory.GetFiles(checkDirectory, $"{input.MesEntity}.csv", SearchOption.TopDirectoryOnly)); } return results; } private static string? GetText(long sequence, List files) { string? result = null; string text; string[] lines; FileInfo fileInfo; foreach (string file in files.OrderByDescending(l => new FileInfo(l).LastWriteTime)) { fileInfo = new FileInfo(file); if (fileInfo.LastWriteTime.Ticks > sequence) continue; lines = File.ReadAllLines(file); if (lines.Length == 0) continue; text = lines.First(); if (string.IsNullOrEmpty(text) || text.Length < 3 || text[0] != '1' || (text[1] != 't' && text[1] != 'T')) continue; result = text; break; } return result; } private static Common Get(Input input, HttpClient httpClient) { int? rds; string? psn; Common result; Task streamTask; Task httpResponseMessageTask; string mid = string.IsNullOrEmpty(input.MID) ? string.Empty : input.MID; JsonSerializerOptions jsonSerializerOptions = new() { PropertyNameCaseInsensitive = true }; int? reactor = mid.Length < 2 || !int.TryParse(mid.Substring(0, 2), out int reactorNumber) ? null : reactorNumber; httpResponseMessageTask = httpClient.GetAsync($"{httpClient.BaseAddress}/reactors/{reactor}"); httpResponseMessageTask.Wait(); if (httpResponseMessageTask.Result.StatusCode != System.Net.HttpStatusCode.OK) throw new Exception($"Unable to OI <{httpResponseMessageTask.Result.StatusCode}>"); streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync(); streamTask.Wait(); if (!streamTask.Result.CanRead) throw new NullReferenceException(nameof(streamTask)); ReactorRoot? reactorRoot = JsonSerializer.Deserialize(streamTask.Result, jsonSerializerOptions); streamTask.Result.Dispose(); if (reactorRoot is null || reactor != reactorRoot.Reactor.ReactorNo || reactorRoot.Reactor.LoadedRDS is null || reactorRoot.Reactor.LoadedRDS.Length < 1) { rds = null; psn = null; result = new(layer: null, psn: psn, rdsNumber: rds, reactor: reactor, zone: null, employee: null, workOrder: null); } else { rds = reactorRoot.Reactor.LoadedRDS[0]; RunDataSheetRoot? runDataSheetRoot = GetRunDataSheetRoot(httpClient, rds.Value); if (runDataSheetRoot is null || reactor != runDataSheetRoot.RunDataSheet.Reactor) { psn = null; result = new(layer: null, psn: psn, rdsNumber: rds, reactor: reactor, zone: null, employee: null, workOrder: null); } else { psn = runDataSheetRoot.RunDataSheet.PSN.ToString(); result = new(layer: null, psn: psn, rdsNumber: rds, reactor: reactor, zone: null, employee: null, workOrder: null); } } return result; } private static RunDataSheetRoot? GetRunDataSheetRoot(HttpClient httpClient, int rds) { RunDataSheetRoot? runDataSheetRoot; JsonSerializerOptions jsonSerializerOptions = new() { PropertyNameCaseInsensitive = true }; using Task httpResponseMessageTask = httpClient.GetAsync($"{httpClient.BaseAddress}/materials/rds/{rds}"); httpResponseMessageTask.Wait(); if (httpResponseMessageTask.Result.StatusCode != System.Net.HttpStatusCode.OK) throw new Exception($"Unable to OI <{httpResponseMessageTask.Result.StatusCode}>"); using Task streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync(); streamTask.Wait(); if (!streamTask.Result.CanRead) throw new NullReferenceException(nameof(streamTask)); runDataSheetRoot = JsonSerializer.Deserialize(streamTask.Result, jsonSerializerOptions); streamTask.Result.Dispose(); return runDataSheetRoot; } private static Common Get(Input input, string barcodeHostFileShare) { Common result; if (string.IsNullOrEmpty(barcodeHostFileShare) || !Directory.Exists(barcodeHostFileShare)) throw new Exception($"Unable to access file-share <{barcodeHostFileShare}>"); int? rds; long sequence = 0; WorkOrder? workOrder; string mid = string.IsNullOrEmpty(input.MID) ? string.Empty : input.MID; int? reactor = mid.Length < 2 || !int.TryParse(mid.Substring(0, 2), out int reactorNumber) ? null : reactorNumber; bool parsed = !string.IsNullOrEmpty(input.Sequence) && long.TryParse(input.Sequence, out sequence); List files; if (!parsed || string.IsNullOrEmpty(input.MID)) files = new(); else files = GetFiles(input, barcodeHostFileShare); string? text = GetText(sequence, files); if (text is null || text.Length < 3) { rds = null; workOrder = null; } else if (!text.Contains('.')) { workOrder = null; rds = !int.TryParse(text.Substring(2), out int rdsNumber) ? null : rdsNumber; } else { rds = null; workOrder = GetWorkOrder(new(input, text.Substring(2))); } result = new(layer: null, psn: null, rdsNumber: rds, reactor: reactor, zone: null, employee: null, workOrder: workOrder); return result; } private static string GetRunJson(string lsl2SQLConnectionString, string commandText) { StringBuilder result = new(); 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(DateTime enteredDateTimeFilter, DateTime loadSignatureDateTimeFilter, int? rds, int? workOrderNumber, int? workOrderCassette, int? slot, int? reactor) { // cSpell:disable List results = new(); int rdsValue = rds is null ? -1 : rds.Value; int slotValue = slot is null ? -1 : slot.Value; int reactorValue = reactor is null ? -1 : reactor.Value; int workOrderNumberValue = workOrderNumber is null ? -1 : workOrderNumber.Value; int workOrderCassetteValue = workOrderCassette is null ? -1 : workOrderCassette.Value; results.Add(" select "); results.Add(" rr.rds_no "); results.Add(" , rr.reactor "); results.Add(" , rr.ps_no "); results.Add(" , rr.load_lock_side "); results.Add(" , rr.reactor_type "); results.Add(" , rr.recipe_name "); results.Add(" , rr.recipe_no "); results.Add(" , rr.spec_type "); results.Add(" , ( "); results.Add(" select max(wm.zone) "); results.Add(" from lsl2sql.dbo.wm_out_slot wm "); results.Add(" where wm.wo_no = rr.wo_no "); results.Add(" and wm.rds = rr.rds_no "); results.Add($" and wm.in_cass_no = {workOrderCassetteValue}"); results.Add($" and wm.slot_no = {slotValue}"); results.Add(" ) zone "); results.Add(" from lsl2sql.dbo.react_run rr "); results.Add($" where rr.rds_no = {rdsValue}"); results.Add($" and rr.enter_dtm > '{enteredDateTimeFilter:yyyy-MM-dd} 00:00:00.000' "); results.Add(" union all "); results.Add(" select "); results.Add(" rr.rds_no "); results.Add(" , rr.reactor "); results.Add(" , rr.ps_no "); results.Add(" , rr.load_lock_side "); results.Add(" , rr.reactor_type "); results.Add(" , rr.recipe_name "); results.Add(" , rr.recipe_no "); results.Add(" , rr.spec_type "); results.Add(" , ( "); results.Add(" select max(wm.zone) "); results.Add(" from lsl2sql.dbo.wm_out_slot wm "); results.Add(" where wm.wo_no = rr.wo_no "); results.Add(" and wm.rds = rr.rds_no "); results.Add($" and wm.in_cass_no = {workOrderCassetteValue}"); results.Add($" and wm.slot_no = {slotValue}"); results.Add(" ) zone "); results.Add(" from lsl2sql.dbo.react_run rr "); results.Add(" where rr.rds_no = ( "); results.Add(" select max(wm.rds) "); results.Add(" from lsl2sql.dbo.wm_out_slot wm "); results.Add($" where wm.wo_no = {workOrderNumberValue}"); results.Add($" and wm.in_cass_no = {workOrderCassetteValue}"); results.Add($" and wm.slot_no = {slotValue}"); results.Add(" ) "); results.Add(" union all "); results.Add(" select "); results.Add(" rr.rds_no "); results.Add(" , rr.reactor "); results.Add(" , rr.ps_no "); results.Add(" , rr.load_lock_side "); results.Add(" , rr.reactor_type "); results.Add(" , rr.recipe_name "); results.Add(" , rr.recipe_no "); results.Add(" , rr.spec_type "); results.Add(" , ( "); results.Add(" select max(wm.zone) "); results.Add(" from lsl2sql.dbo.wm_out_slot wm "); results.Add(" where wm.wo_no = rr.wo_no "); results.Add(" and wm.rds = rr.rds_no "); results.Add($" and wm.in_cass_no = {workOrderCassetteValue}"); results.Add($" and wm.slot_no = {slotValue}"); results.Add(" ) zone "); results.Add(" from lsl2sql.dbo.react_run rr "); results.Add(" where rr.rds_no = ( "); results.Add(" select max(qa.rds_no) "); results.Add(" from lsl2sql.dbo.react_run qa "); results.Add(" where qa.load_sig != '' "); results.Add($" and qa.load_sig_dtm > '{loadSignatureDateTimeFilter:yyyy-MM-dd} 00:00:00.000' "); results.Add($" and qa.reactor = {reactorValue}"); results.Add(" ) "); results.Add(" for json path "); return string.Join(Environment.NewLine, results); } // cSpell:restore private static CommonB Get(string lsl2SQLConnectionString, DateTime enteredDateTimeFilter, DateTime loadSignatureDateTimeFilter, Common common) { int? rdsNumber; string? psn; string? zone; string? layer; const int zero = 0; int? reactorNumber; string? reactorType; string? loadLockSide; string commandText = GetCommandText(enteredDateTimeFilter, loadSignatureDateTimeFilter, rds: null, workOrderNumber: common.WorkOrder?.WorkOrderNumber, workOrderCassette: common.WorkOrder?.WorkOrderCassette, slot: common.WorkOrder?.SlotNumber, reactor: common.ReactorNumber); string json = GetRunJson(lsl2SQLConnectionString, commandText); if (string.IsNullOrEmpty(json)) { psn = common.PSN; rdsNumber = null; reactorType = null; zone = common.Zone; loadLockSide = null; layer = common.Layer; reactorNumber = common.ReactorNumber; } else { Run[]? runs; try { runs = JsonSerializer.Deserialize(json); } catch (Exception) { runs = Array.Empty(); } if (runs is null || runs.Length == 0) { psn = common.PSN; rdsNumber = null; reactorType = null; zone = common.Zone; loadLockSide = null; layer = common.Layer; reactorNumber = common.ReactorNumber; } else { reactorType = null; Run run = runs[zero]; rdsNumber = run.RdsNo; reactorNumber = run.Reactor; loadLockSide = run.LoadLockSide; psn = string.IsNullOrEmpty(common.PSN) ? run.PSN : common.PSN; zone = string.IsNullOrEmpty(common.Zone) ? run.Zone : common.Zone; layer = string.IsNullOrEmpty(common.Layer) ? run.EpiLayer : common.Layer; } } return new(layer: layer, loadLockSide: loadLockSide, rdsNumber: rdsNumber, psn: psn, reactorNumber: reactorNumber, reactorType: reactorType, zone: zone); } private static CommonB GetWithValidRDS(string lsl2SQLConnectionString, DateTime enteredDateTimeFilter, DateTime loadSignatureDateTimeFilter, string? layer, string? psn, int? rdsNumber, int? reactorNumber, string? zone) { const int zero = 0; string? reactorType; string? loadLockSide; string commandText = GetCommandText(enteredDateTimeFilter, loadSignatureDateTimeFilter, rds: rdsNumber, workOrderNumber: null, workOrderCassette: null, slot: null, reactor: null); string json = GetRunJson(lsl2SQLConnectionString, commandText); if (string.IsNullOrEmpty(json)) { zone = null; reactorType = null; loadLockSide = null; } else { Run[]? runs; try { runs = JsonSerializer.Deserialize(json); } catch (Exception) { runs = Array.Empty(); } if (runs is null || runs.Length == 0) { zone = null; reactorType = null; loadLockSide = null; } else { Run run = runs[zero]; if (string.IsNullOrEmpty(psn)) psn = run.PSN; if (string.IsNullOrEmpty(zone)) zone = run.Zone; if (string.IsNullOrEmpty(layer)) layer = run.EpiLayer; reactorNumber = run.Reactor is null ? reactorNumber : run.Reactor.Value; loadLockSide = run.LoadLockSide; reactorType = run.ReactorType; } } return new(layer: layer, loadLockSide: loadLockSide, rdsNumber: rdsNumber, reactorType: reactorType, psn: psn, reactorNumber: reactorNumber, zone: zone); } }