using Adaptation.Ifx.Eaf.EquipmentConnector.File.Configuration; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net.WebSockets; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; namespace Adaptation.FileHandlers.wc; public class WaferCounterHelper { #nullable enable private class Record { public int Check { get; set; } public int Total { get; set; } public string? SlotMap { get; set; } } private class WaferCounter { public DateTime DateTime { get; set; } public string? DateTimeFormatted { get; set; } public string? EquipmentId { get; set; } public string? Text { get; set; } public int Total { get; set; } public string? SlotMap { get; set; } } private static long? _Wait; private static string? _Directory; private static readonly Dictionary _ClientWebSockets = new(); internal static void Initialize(FileConnectorConfiguration fileConnectorConfiguration) { _Directory = fileConnectorConfiguration.TargetFileLocation; _Wait = fileConnectorConfiguration.IdleEventWaitTimeInSeconds; } internal static string[] Review() { List results = new(); lock (_ClientWebSockets) { foreach (KeyValuePair keyValuePair in _ClientWebSockets) { if (results.Contains(keyValuePair.Key)) continue; results.Add(keyValuePair.Key); } } return results.ToArray(); } internal static void Subscribe(string callbackUrl) { if (!_ClientWebSockets.ContainsKey(callbackUrl)) { ClientWebSocket clientWebSocket = new(); Task task = clientWebSocket.ConnectAsync(new Uri(callbackUrl), CancellationToken.None); task.Wait(); lock (_ClientWebSockets) _ClientWebSockets.Add(callbackUrl, clientWebSocket); } } private static void CloseAndDispose(string callbackUrl, ClientWebSocket clientWebSocket) { Task task = clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); task.Wait(); clientWebSocket.Dispose(); lock (_ClientWebSockets) _ = _ClientWebSockets.Remove(callbackUrl); } internal static void Unsubscribe(string callbackUrl) { if (_ClientWebSockets.TryGetValue(callbackUrl, out ClientWebSocket? clientWebSocket)) CloseAndDispose(callbackUrl, clientWebSocket); } private static Record GetRecord(string line1, string line2) { Record result; string? slotMap = string.IsNullOrEmpty(line2) || line2.Length != 8 ? null : line2.Substring(1, 1); int check = slotMap == "1" ? 1 : 0; int total = int.Parse(line1.Substring(1)); foreach (char item in line2.Substring(2)) { switch (item) { case '0': check += 0; slotMap += "0000"; break; case '1': check += 1; slotMap += "0001"; break; case '2': check += 1; slotMap += "0010"; break; case '3': check += 2; slotMap += "0011"; break; case '4': check += 1; slotMap += "0100"; break; case '5': check += 2; slotMap += "0101"; break; case '6': check += 2; slotMap += "0110"; break; case '7': check += 3; slotMap += "0111"; break; case '8': check += 1; slotMap += "1000"; break; case '9': check += 2; slotMap += "1001"; break; case 'A': check += 2; slotMap += "1010"; break; case 'B': check += 3; slotMap += "1011"; break; case 'C': check += 2; slotMap += "1100"; break; case 'D': check += 3; slotMap += "1101"; break; case 'E': check += 3; slotMap += "1110"; break; case 'F': check += 4; slotMap += "1111"; break; default: break; } } result = new Record { Check = check, Total = total, SlotMap = slotMap }; return result; } internal static void TestSlotMap(string line1, string line2, string expectedSlotMap) { Record record = GetRecord(line1, line2); if (record.SlotMap != expectedSlotMap) throw new NotSupportedException($"{line1}{line2}{expectedSlotMap}"); } internal static string GetJson(string cellInstanceName, DateTime dateTime, string contents) { string result; string[] separator = new string[] { "\r\n" }; string[] lines = contents.Split(separator, StringSplitOptions.None); if (lines.Length <= 1) throw new Exception("Incomplete file length!"); Record record = GetRecord(lines[0], lines[1]); if (string.IsNullOrEmpty(record.SlotMap) || record.SlotMap.Length != 25) throw new Exception("Wrong length!"); if (record.Total != record.Check) throw new Exception("Invalid!"); WaferCounter waferCounter = new() { DateTime = dateTime, DateTimeFormatted = dateTime.ToString("yyyy-MM-dd hh:mm tt"), EquipmentId = cellInstanceName, Text = contents, Total = record.Total, SlotMap = record.SlotMap }; result = JsonSerializer.Serialize(waferCounter); return result; } private static void Notify(string json, List> clientWebSockets) { List> removeClientWebSockets = new(); ArraySegment bytes = new(Encoding.Default.GetBytes(json)); foreach (KeyValuePair clientWebSocket in clientWebSockets) { try { Task task = clientWebSocket.Value.SendAsync(bytes, WebSocketMessageType.Binary, false, CancellationToken.None); task.Wait(); } catch (Exception) { removeClientWebSockets.Add(clientWebSocket); } } if (removeClientWebSockets.Count > 0) { lock (_ClientWebSockets) { foreach (KeyValuePair clientWebSocket in removeClientWebSockets) CloseAndDispose(clientWebSocket.Key, clientWebSocket.Value); } } } private static void Archive(FileConnectorConfiguration fileConnectorConfiguration, Calendar calendar, DateTime dateTime, string fileName, string fullFileName) { string weekOfYear = $"{dateTime:yyyy}_Week_{calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}"; string archiveDirectory = Path.Combine(fileConnectorConfiguration.TargetFileLocation, "Archive", weekOfYear); if (!Directory.Exists(archiveDirectory)) _ = Directory.CreateDirectory(archiveDirectory); string to = Path.Combine(archiveDirectory, fileName); File.Move(fullFileName, to); } private static void MoveFile(string waferSizeDirectory, FileInfo fileInfo) { Calendar calendar = new CultureInfo("en-US").Calendar; string weekOfYear = $"{fileInfo.LastWriteTime:yyyy}_Week_{calendar.GetWeekOfYear(fileInfo.LastWriteTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}"; string checkDirectory = Path.Combine(waferSizeDirectory, "Archive", weekOfYear); if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); string checkFile = Path.Combine(checkDirectory, fileInfo.Name); if (!File.Exists(checkFile)) File.Move(fileInfo.FullName, checkFile); } internal static void Notify(FileConnectorConfiguration fileConnectorConfiguration, Calendar calendar, DateTime dateTime, string fileName, string fullFileName, string json) { List> clientWebSockets = new(); lock (_ClientWebSockets) { foreach (KeyValuePair keyValuePair in _ClientWebSockets) clientWebSockets.Add(keyValuePair); } if (clientWebSockets.Count > 0) { Notify(json, clientWebSockets); Archive(fileConnectorConfiguration, calendar, dateTime, fileName, fullFileName); } } private static FileInfo[] GetFileInfoCollection(string waferSizeDirectory) { List results = new(); List files = new(); FileInfo[] fileInfoCollection; DateTime dateTime = DateTime.Now; long ticks = dateTime.AddSeconds(1).Ticks; for (int i = 0; i < int.MaxValue; i++) { files.AddRange(!Directory.Exists(waferSizeDirectory) ? Array.Empty() : Directory.GetFiles(waferSizeDirectory, "*.wc", SearchOption.TopDirectoryOnly)); if (results.Count > 0 || DateTime.Now.Ticks > ticks) break; Thread.Sleep(250); } fileInfoCollection = (from l in files select new FileInfo(l)).OrderByDescending(l => l.LastWriteTime).ToArray(); if (fileInfoCollection.Length > 0) results.Add(fileInfoCollection[0]); for (int i = 0; i < fileInfoCollection.Length; i++) MoveFile(waferSizeDirectory, fileInfoCollection[i]); return fileInfoCollection; } private static WaferCounter GetLastQuantityAndSlotMapWithText(string waferSize, string text, FileInfo fileInfo) { WaferCounter result; string[] lines = File.ReadAllLines(fileInfo.FullName); if (lines.Length <= 1) throw new Exception("Incomplete file length!"); string[] segments = fileInfo.Name.Split('-'); Record record = GetRecord(lines[0], lines[1]); string equipmentId = segments.Length < 2 ? fileInfo.Name : segments[1].Split('.')[0]; if (string.IsNullOrEmpty(record.SlotMap) || record.SlotMap.Length != 25) throw new Exception("Wrong length!"); if (record.Total != record.Check) throw new Exception("Invalid!"); result = new() { DateTime = fileInfo.LastWriteTime, DateTimeFormatted = fileInfo.LastWriteTime.ToString("yyyy-MM-dd hh:mm tt"), EquipmentId = $"WC{waferSize}{equipmentId}", Text = text, Total = record.Total, SlotMap = record.SlotMap }; return result; } private static string GetWaferSizeDirectory(string area, string waferSize) => Path.Combine(_Directory ?? throw new NotSupportedException(), area, waferSize); internal static string GetLastQuantityAndSlotMap(string area, string waferSize) { string result; WaferCounter waferCounter; string waferSizeDirectory = GetWaferSizeDirectory(area, waferSize); FileInfo[] fileInfoCollection = GetFileInfoCollection(waferSizeDirectory); if (fileInfoCollection.Length == 0) throw new Exception("No files!"); string text = string.Empty; waferCounter = GetLastQuantityAndSlotMapWithText(waferSize, text, fileInfoCollection[0]); result = JsonSerializer.Serialize(waferCounter); return result; } }