290 lines
11 KiB
C#

using System;
using System.Collections.Generic;
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
{
public string AutomationMode { get; }
public string BasicType { get; }
public string EpiLayer { get; }
public string Equipment { get; }
public string JobName { get; }
public string LotName { get; }
public string PackageName { get; }
public string ProcessSpecName { get; }
public string ProcessType { get; }
public string ProductName { get; }
public string Qty { get; }
public string RecipeName { get; }
public string StateModel { get; }
//
public bool IsAreaSi { get; }
public DateTime DateTime { get; }
public List<Item> Items { get; }
public Job(string lsl2SQLConnectionString, string metrologyFileShare, string mid)
{
const int zero = 0;
Items = new List<Item>();
if (string.IsNullOrEmpty(mid) || mid[zero] != '{' || mid[mid.Length - 1] != '}' || !mid.Contains("\"Si\""))
IsAreaSi = false;
else
{
int rds;
string psn;
string lotName;
string reactor;
string epiLayer;
string basicType;
const string hyphen = "-";
Input input = JsonSerializer.Deserialize<Input>(mid);
IsAreaSi = input.Area == "Si";
if (input.MID is null)
input.MID = string.Empty;
if (!long.TryParse(input.Sequence, out long sequence))
DateTime = DateTime.Now;
else
DateTime = new DateTime(sequence);
if (input.MID.Length is not 2 and not 3)
(psn, rds, reactor) = Get(input);
else
(psn, rds, reactor) = Get(metrologyFileShare, input);
AutomationMode = string.Concat(DateTime.Ticks, ".", input.MesEntity);
Equipment = input.MesEntity;
JobName = DateTime.Ticks.ToString();
if (!IsValid(rds))
(basicType, epiLayer, lotName, psn, reactor) = Get(lsl2SQLConnectionString, input, psn, rds, reactor);
else
{
basicType = hyphen;
lotName = input.MID;
epiLayer = "1";
}
EpiLayer = epiLayer;
BasicType = basicType;
LotName = lotName;
PackageName = hyphen; //WAFER_ID WaferLot
ProcessSpecName = hyphen; //WAFER_POS PocketNumber
ProcessType = reactor;
ProductName = psn;
Qty = "1";
RecipeName = input.Recipe;
StateModel = input.EquipmentType;
Items.Add(new Item { Name = "0", Type = "NA", Number = (0 + 1).ToString(), Qty = "1", CarrierName = hyphen });
}
}
private static bool IsValid(int rds) => rds is < 100000 or > 100000000;
private static (string, int, string) Get(Input input)
{
int rds;
string psn;
string reactor;
const int zero = 0;
string[] segments = Regex.Split(input.MID, @"[^0-9']");
if (segments.Length < 3)
rds = 0;
else
_ = int.TryParse(segments[1], out rds);
if (IsValid(rds))
{
psn = string.Empty;
reactor = string.Empty;
}
else
{
psn = segments[2];
reactor = segments[zero];
}
return new(psn, rds, reactor);
}
private static (string, int, string) Get(string metrologyFileShare, Input input)
{
int rds = 0;
string psn;
string reactor;
string[] lines;
string moveFile;
psn = string.Empty;
reactor = 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);
string[] 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 rds) || IsValid(rds))
continue;
break;
}
if (rds != 0)
{
moveFile = Path.Combine(usedDirectory, Path.GetFileName(file));
if (File.Exists(moveFile))
File.Delete(file);
else
File.Move(file, moveFile);
break;
}
}
return new(psn, rds, reactor);
}
private static string GetRunJson(string lsl2SQLConnectionString, int rds)
{
string result;
object scalar = null;
StringBuilder sql = new();
_ = sql.Append(" select ").
Append(" qa.rds_no ").
Append(" , qa.max_rds_layer_keys_mv_no ").
Append(" , qa.max_part_no ").
Append(" , substring(max_part_no, 0, len(max_part_no)) max_part_no_substr_0 ").
Append(" , substring(max_part_no, 1, len(max_part_no)) max_part_no_substr_1 ").
Append(" , rr.part_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(" , react_tool_id ").
Append(" , epi_layer ").
Append(" , epi_step ").
Append(" , epi_step_lsid ").
Append(" , epi_dopant ").
Append(" , epi_thick_min ").
Append(" , epi_thick_max ").
Append(" , epi_thick_targ ").
Append(" , epi_res_min ").
Append(" , epi_res_max ").
Append(" , epi_res_targ ").
Append(" from ( ").
Append(" select top (1000) ").
Append(" rds_no ").
Append(" , ( ").
Append(" select max(mv_no) ").
Append(" from lsl2sql.dbo.rds_rds_layer_keys ").
Append(" where seq = rds_no ").
Append(" ) max_rds_layer_keys_mv_no ").
Append(" , case when part_no != '' ").
Append(" then part_no ").
Append(" else ").
Append(" ( ").
Append(" select max(part_no) ").
Append(" from lsl2sql.dbo.react_run b ").
Append(" where b.ps_no = qa.ps_no ").
Append(" ) ").
Append(" end max_part_no ").
Append(" from lsl2sql.dbo.react_run qa ").
Append(" where rds_no = '").Append(rds).Append("' ").
Append(" ) as qa ").
Append(" inner join lsl2sql.dbo.react_run rr ").
Append(" on qa.rds_no = rr.rds_no ").
Append(" left join lsl2sql.dbo.epi_part_layer_spec es ").
Append(" on max_part_no = epi_pn ").
Append(" or substring(max_part_no, 0, len(max_part_no)) = epi_pn ").
Append(" or substring(max_part_no, 1, len(max_part_no)) = epi_pn ").
Append(" order by epi_layer ").
Append(" for json path ");
try
{
using SqlConnection sqlConnection = new(lsl2SQLConnectionString);
sqlConnection.Open();
using (SqlCommand sqlCommand = new(sql.ToString(), sqlConnection))
scalar = sqlCommand.ExecuteScalar();
sqlConnection.Close();
}
catch (Exception)
{ }
if (scalar is null)
result = string.Empty;
else
result = scalar.ToString();
return result;
}
private static (string, string, string, string, string) Get(string lsl2SQLConnectionString, Input input, string psn, int rds, string reactor)
{
string lotName;
string epiLayer;
string basicType;
const int zero = 0;
const string hyphen = "-";
lotName = rds.ToString();
string json = GetRunJson(lsl2SQLConnectionString, rds);
if (string.IsNullOrEmpty(json))
{
epiLayer = "1";
basicType = hyphen;
}
else
{
Run[] runs;
try
{ runs = JsonSerializer.Deserialize<Run[]>(json); }
catch (Exception)
{ runs = Array.Empty<Run>(); }
if (!runs.Any())
{
epiLayer = "1";
basicType = hyphen;
}
else
{
if (string.IsNullOrEmpty(reactor))
reactor = runs[zero].Reactor.ToString();
if (string.IsNullOrEmpty(psn))
psn = runs[zero].PSN;
string loadLockSide = runs[zero].LoadLockSide;
string loadLockSideFull = loadLockSide switch
{
"L" => "Left",
"R" => "Right",
_ => loadLockSide,
};
basicType = $"{loadLockSideFull} - {runs[zero].ReactorType}";
int maxRdsLayerKeysMvNo = runs[zero].MaxRdsLayerKeysMvNo;
if (maxRdsLayerKeysMvNo == 1)
epiLayer = "1";
else
{
if (string.IsNullOrEmpty(input.MeanThickness) || !double.TryParse(input.MeanThickness, out double meanThickness))
epiLayer = runs[zero].EpiLayer;
else
{
epiLayer = hyphen;
foreach (Run run in runs)
{
if (run.EpiThickMin is null || run.EpiThickMax is null)
continue;
if (meanThickness < run.EpiThickMin || meanThickness > run.EpiThickMax)
continue;
epiLayer = run.EpiLayer;
}
}
}
}
}
return new(basicType, epiLayer, lotName, psn, reactor);
}
}