From 2afb3120655ce6314dbe962d317ccb551bb6bf2e Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Fri, 21 Feb 2025 11:13:56 -0700 Subject: [PATCH] Bump AzureDevOpsRepository Markdown links Ticks bug fix, default to *.wc files and formatting --- Server/OI.Metrology.Server.csproj | 22 +- Server/Repositories/ExportRepository.cs | 7 +- Server/Repositories/FileShareRepository.cs | 11 +- Shared/DataModels/WaferCounterArchive.cs | 36 +++ Shared/Models/CharacterizationParameters.cs | 2 +- Shared/Models/PollValue.cs | 17 ++ .../Stateless/IAzureDevOpsController.cs | 16 + .../Stateless/IAzureDevOpsRepository.cs | 8 + .../Models/Stateless/IFileShareRepository.cs | 8 +- Static/favicon.ico | Bin 0 -> 2042 bytes Static/files.html | 68 ++--- Static/js/files.js | 7 +- Static/js/leo.js | 161 ---------- Static/js/mes.js | 161 ---------- Static/js/site-server.js | 281 +++++++++++++++--- Static/leo.html | 43 --- Static/mes.html | 43 --- Static/styles/files.css | 12 + Tests/OI.Metrology.Tests.csproj | 18 +- Tests/UnitTestFileShareController.cs | 28 +- Wafer-Counter/.vscode/tasks.json | 10 + .../ApiControllers/AzureDevOpsController.cs | 26 ++ .../ApiControllers/FileShareController.cs | 42 +-- Wafer-Counter/Helper/ParameterHelper.cs | 82 +++++ .../{Repositories => Helper}/RegexHelper.cs | 4 +- Wafer-Counter/Models/AppSettings.cs | 12 +- Wafer-Counter/Models/Binder/AppSettings.cs | 7 +- .../OI.Metrology.Wafer.Counter.csproj | 20 +- Wafer-Counter/Program.cs | 1 + .../Repositories/AzureDevOpsRepository.cs | 25 ++ .../Repositories/FileShareRepository.cs | 54 +++- .../Repositories/WaferCounterRepository.cs | 22 +- 32 files changed, 661 insertions(+), 593 deletions(-) create mode 100644 Shared/DataModels/WaferCounterArchive.cs create mode 100644 Shared/Models/PollValue.cs create mode 100644 Shared/Models/Stateless/IAzureDevOpsController.cs create mode 100644 Shared/Models/Stateless/IAzureDevOpsRepository.cs create mode 100644 Static/favicon.ico delete mode 100644 Static/js/leo.js delete mode 100644 Static/js/mes.js delete mode 100644 Static/leo.html delete mode 100644 Static/mes.html create mode 100644 Static/styles/files.css create mode 100644 Wafer-Counter/ApiControllers/AzureDevOpsController.cs create mode 100644 Wafer-Counter/Helper/ParameterHelper.cs rename Wafer-Counter/{Repositories => Helper}/RegexHelper.cs (78%) create mode 100644 Wafer-Counter/Repositories/AzureDevOpsRepository.cs diff --git a/Server/OI.Metrology.Server.csproj b/Server/OI.Metrology.Server.csproj index 1bbd9b6..b3642ec 100644 --- a/Server/OI.Metrology.Server.csproj +++ b/Server/OI.Metrology.Server.csproj @@ -28,19 +28,19 @@ - - - - - - - - - + + + + + + + + + - + - + diff --git a/Server/Repositories/ExportRepository.cs b/Server/Repositories/ExportRepository.cs index ad6eb62..86ef7bc 100644 --- a/Server/Repositories/ExportRepository.cs +++ b/Server/Repositories/ExportRepository.cs @@ -2,6 +2,7 @@ using OI.Metrology.Server.Models; using OI.Metrology.Shared.DataModels; using OI.Metrology.Shared.Models; using OI.Metrology.Shared.Models.Stateless; +using System.Collections.ObjectModel; using System.Data; using System.Globalization; using System.Text; @@ -44,15 +45,15 @@ public class ExportRepository : IExportRepository List results = new(); Uri uri; string[] weeks = Get(); - List nginxFileSystemSortableCollection; + ReadOnlyCollection collection; foreach (string weekYear in weeks) { if (headerCommon.ID < 1) uri = _FileShareRepository.Append(new Uri(_AppSettings.EcMesaFileShareMetrologySi), "Archive", "API", weekYear, $"-{headerCommon.PSN}", $"-{headerCommon.Reactor}", $"-{headerCommon.RDS}"); else uri = _FileShareRepository.Append(new Uri(_AppSettings.EcMesaFileShareMetrologySi), "Archive", "API", weekYear, $"-{headerCommon.PSN}", $"-{headerCommon.Reactor}", $"-{headerCommon.RDS}", $"-{headerCommon.ID}"); - nginxFileSystemSortableCollection = _FileShareRepository.GetNginxFileSystemSortableCollection(httpClient, uri, endsWith); - results.AddRange(nginxFileSystemSortableCollection); + collection = _FileShareRepository.GetNginxFileSystemSortableCollection(httpClient, uri, endsWith); + results.AddRange(collection); } return results.OrderByDescending(l => l.DateTime).ToArray(); } diff --git a/Server/Repositories/FileShareRepository.cs b/Server/Repositories/FileShareRepository.cs index cd3bcf5..ec48542 100644 --- a/Server/Repositories/FileShareRepository.cs +++ b/Server/Repositories/FileShareRepository.cs @@ -1,5 +1,7 @@ +using OI.Metrology.Shared.DataModels; using OI.Metrology.Shared.Models; using OI.Metrology.Shared.Models.Stateless; +using System.Collections.ObjectModel; using System.Text.Json; using System.Web; @@ -78,7 +80,7 @@ public class FileShareRepository : IFileShareRepository return result; } - List IFileShareRepository.GetNginxFileSystemSortableCollection(HttpClient httpClient, Uri uri, string? endsWith) + ReadOnlyCollection IFileShareRepository.GetNginxFileSystemSortableCollection(HttpClient httpClient, Uri uri, string? endsWith) { List results = new(); Task httpResponseMessage = httpClient.GetAsync(uri); @@ -97,10 +99,13 @@ public class FileShareRepository : IFileShareRepository results.Add(nginxFileSystemSortable); } } - return results; + return new(results); } - List IFileShareRepository.GetArchiveData(CharacterizationParameters archiveParameters) => + ReadOnlyCollection IFileShareRepository.GetArchiveData(CharacterizationParameters archiveParameters) => + throw new NotImplementedException(); + + ReadOnlyCollection IFileShareRepository.GetEquipmentIds() => throw new NotImplementedException(); } \ No newline at end of file diff --git a/Shared/DataModels/WaferCounterArchive.cs b/Shared/DataModels/WaferCounterArchive.cs new file mode 100644 index 0000000..5b2ae24 --- /dev/null +++ b/Shared/DataModels/WaferCounterArchive.cs @@ -0,0 +1,36 @@ +using System.Text.Json.Serialization; + +namespace OI.Metrology.Shared.DataModels; + +public class WaferCounterArchive +{ + + public long ID { get; set; } + public DateTime InsertDate { get; set; } + public Guid AttachmentID { get; set; } + public string? Title { get; set; } + public DateTime Date { get; set; } + public long ToolTypeID { get; set; } + public string? ToolTypeName { get; set; } + + public string? MesEntity { get; set; } + + public string? Employee { get; set; } + public string? Layer { get; set; } + public string? PSN { get; set; } + public string? RDS { get; set; } + public string? Reactor { get; set; } + public string? Recipe { get; set; } + public string? Zone { get; set; } + + public string? SlotMap { get; set; } + public string? Text { get; set; } + public int? Total { get; set; } + +} + +[JsonSourceGenerationOptions(WriteIndented = true, NumberHandling = JsonNumberHandling.AllowReadingFromString)] +[JsonSerializable(typeof(WaferCounterArchive))] +public partial class WaferCounterArchiveSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Shared/Models/CharacterizationParameters.cs b/Shared/Models/CharacterizationParameters.cs index fa96fda..18275a0 100644 --- a/Shared/Models/CharacterizationParameters.cs +++ b/Shared/Models/CharacterizationParameters.cs @@ -13,4 +13,4 @@ public record CharacterizationParameters([property: JsonPropertyName("area")] st [JsonSerializable(typeof(CharacterizationParameters))] public partial class CharacterizationParametersSourceGenerationContext : JsonSerializerContext { -} +} \ No newline at end of file diff --git a/Shared/Models/PollValue.cs b/Shared/Models/PollValue.cs new file mode 100644 index 0000000..277b542 --- /dev/null +++ b/Shared/Models/PollValue.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; + +namespace OI.Metrology.Shared.Models; + +public record PollValue(string? Json, + [property: JsonPropertyName("id")] int? Id, + [property: JsonPropertyName("page")] string? Page, + string? QueryString, + string? RemoteIpAddress, + [property: JsonPropertyName("time")] long? Time, + [property: JsonPropertyName("value")] int? Value); + +[JsonSourceGenerationOptions(WriteIndented = true, NumberHandling = JsonNumberHandling.AllowReadingFromString)] +[JsonSerializable(typeof(PollValue))] +public partial class PollValueSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Shared/Models/Stateless/IAzureDevOpsController.cs b/Shared/Models/Stateless/IAzureDevOpsController.cs new file mode 100644 index 0000000..31998d2 --- /dev/null +++ b/Shared/Models/Stateless/IAzureDevOpsController.cs @@ -0,0 +1,16 @@ +namespace OI.Metrology.Shared.Models.Stateless; + +public interface IAzureDevOpsController +{ + + enum Action : int + { + Index = 0, + Save = 1 + } + + static string GetRouteName() => nameof(IAzureDevOpsController)[1..^10]; + + T Save(); + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/IAzureDevOpsRepository.cs b/Shared/Models/Stateless/IAzureDevOpsRepository.cs new file mode 100644 index 0000000..c84e08c --- /dev/null +++ b/Shared/Models/Stateless/IAzureDevOpsRepository.cs @@ -0,0 +1,8 @@ +namespace OI.Metrology.Shared.Models.Stateless; + +public interface IAzureDevOpsRepository +{ + + void Save(PollValue pollValue); + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/IFileShareRepository.cs b/Shared/Models/Stateless/IFileShareRepository.cs index b92546c..1e4e731 100644 --- a/Shared/Models/Stateless/IFileShareRepository.cs +++ b/Shared/Models/Stateless/IFileShareRepository.cs @@ -1,3 +1,6 @@ +using OI.Metrology.Shared.DataModels; +using System.Collections.ObjectModel; + namespace OI.Metrology.Shared.Models.Stateless; public interface IFileShareRepository @@ -7,9 +10,10 @@ public interface IFileShareRepository void MoveFile(string from, string to); Uri Append(Uri uri, params string[] paths); void FileWrite(string path, string contents); + ReadOnlyCollection GetEquipmentIds(); HttpResponseMessage ReadFile(HttpClient httpClient, Uri uri); void CopyFile(HttpClient httpClient, string from, string to); void MoveFile(HttpClient httpClient, string from, string to); - List GetArchiveData(CharacterizationParameters characterizationParameters); - List GetNginxFileSystemSortableCollection(HttpClient httpClient, Uri uri, string? endsWith); + ReadOnlyCollection GetArchiveData(CharacterizationParameters characterizationParameters); + ReadOnlyCollection GetNginxFileSystemSortableCollection(HttpClient httpClient, Uri uri, string? endsWith); } \ No newline at end of file diff --git a/Static/favicon.ico b/Static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e9b409d36ce67e04adcc48625690e3db91dfb506 GIT binary patch literal 2042 zcmb`|duYvJ9LMqRZ<}*mx0#ty%-qRF?dD#tVT6qRp|iQnwD!m35;1L~hUC7vO~`dN zC8XSDlRuvHl#eWQMR*;dI;G~;URpBk)eXv=bZdUpI_6^*F!W!Nj7rFbY`%GD)&WPQ1{0Y`I` zAswZdh}FozI>ey@DcFJuSc4%rjcN>l`mSO+0+<6mm&$syZ|z_4XW=tW^)WZHKHp+^ zrWVt12J=yc890hW)L}f%BGkX0`YxnGeV4EtHQ0&*9D?=}zKFXL-%vO+=C1Ol_!sb} z@#pcZ_Pi4f(AW)7E)?q`wqpg_i$96j@3Ac}`Jj=TpX1;1#%lhBxB}%}b1Lt*p}940 zF%CjG%SSynLv7n|1R6gM;fuI_97Qu{bjQ8xBszi?6bKnzfb&lB!Pwwz z{f1X!Jr-ju1|SX{5rJSH+s^l|_n~1{&jq}+}tl_NzG2P jzp2cvD7Qgcb`<=v9@Q%*7d&aY=ei$~U}dgbUgmxW+tvDD literal 0 HcmV?d00001 diff --git a/Static/files.html b/Static/files.html index 68bfeb1..f9f3734 100644 --- a/Static/files.html +++ b/Static/files.html @@ -6,25 +6,25 @@ File(s) - + - - + - - - + + + - - - - - + + + + + - - + + @@ -64,7 +64,7 @@
- +
@@ -94,7 +94,7 @@
-
+
@@ -103,46 +103,14 @@
-
- -
-
- -
-
- -
-
- -

@@ -153,9 +121,9 @@
- - - + + + \ No newline at end of file diff --git a/Static/js/files.js b/Static/js/files.js index f4432eb..9c07f10 100644 --- a/Static/js/files.js +++ b/Static/js/files.js @@ -1,10 +1,5 @@ $(document).ready(function () { - const queryString = window.location.search; - const urlParams = new URLSearchParams(queryString); - const initialHeaderId = urlParams.get('headerid'); - const initialToolTypeID = urlParams.get('tooltypeid'); - const initialHeaderAttachmentId = urlParams.get('headerattachmentid'); - initFiles("https://oi-metrology-viewer-prod.mes.infineon.com:4433/api", "https://oi-metrology-viewer-prod.mes.infineon.com", initialToolTypeID, initialHeaderId, initialHeaderAttachmentId); + initFiles("https://oi-metrology-viewer-prod.mes.infineon.com:4438/api", "https://oi-metrology-viewer-prod.mes.infineon.com", "https://eaf-prod.mes.infineon.com:4439"); }); \ No newline at end of file diff --git a/Static/js/leo.js b/Static/js/leo.js deleted file mode 100644 index ca62889..0000000 --- a/Static/js/leo.js +++ /dev/null @@ -1,161 +0,0 @@ -function compareFunction(a, b) { - return a.Priority[0] - b.Priority[0] || a.TimeCriticality[0] - b.TimeCriticality[0] || b.State[0] - a.State[0] || a.Id - b.Id; -} - -function showOne(rowData) { - if (rowData == null) - return; - var data = []; - data.push({ name: "ADO Edit", value: '' + rowData["Id"] + '' }); - for (const property in rowData) { - if (rowData[property] == null) - continue; - data.push({ name: property, value: rowData[property].toString() }); - } - $("#AllGrid").igGrid({ - autoGenerateColumns: true, - dataSource: data, - width: "100%", - showHeader: false, - }); -} - -function loadOne() { - var selectedRow = $("#HeaderGrid").data("igGridSelection").selectedRow(); - if (selectedRow == null) - return; - var rowData = $("#HeaderGrid").data("igGrid").dataSource.dataView()[selectedRow.index]; - showOne(rowData); -} - -function detailSelectionChangedRunInfo(evt, ui) { - if (ui.row.index === 0) - return; - var rowData = ui.owner.grid.dataSource.dataView()[ui.row.index]; - showOne(rowData); -} - -function getState(state) { - var result; - if (state == null) - result = "9-Null"; - else if (state === "New") - result = `1-${state}`; - else if (state === "Active") - result = `2-${state}`; - else if (state === "Resolved") - result = `3-${state}`; - else if (state === "Closed") - result = `4-${state}`; - else if (state === "Removed") - result = `5-${state}`; - else - result = `8-${state}`; - return result; -} - -function getPriority(workItemType, priority) { - var result; - if (workItemType === "Bug") - result = "0-Bug"; - else if (priority == null || priority === 0) - result = "9-Null"; - else if (priority === 1) - result = `${priority}-High`; - else if (priority === 2) - result = `${priority}-Med`; - else if (priority === 3) - result = `${priority}-Low`; - else if (priority === 4) - result = `${priority}-TBD`; - else - result = "8-Not"; - return result; -} - -function getTimeCriticality(workItemType, timeCriticality) { - var result; - if (workItemType === "Bug") - result = "0-Bug"; - else if (timeCriticality == null || timeCriticality === 0) - result = "9-Null"; - else if (timeCriticality === 1) - result = `${timeCriticality}-QSM`; - else if (timeCriticality === 2) - result = `${timeCriticality}-Qual`; - else if (timeCriticality === 3) - result = `${timeCriticality}-Eff`; - else - result = "8-Not"; - return result; -} - -function getWorkItems(data) { - var workItems = []; - var workItem; - for (var i = data.length - 1; i > -1; i--) { - workItem = data[i]; - if (workItem.AreaPath !== 'ART SPS\\LEO') - continue; - if (workItem.Tags != null && workItem.Tags.includes("Ignore")) - continue; - if (workItem.WorkItemType !== 'Feature' && workItem.WorkItemType !== 'Bug') - continue; - workItem["State"] = getState(workItem["State"]) - workItem["Priority"] = getPriority(workItem["WorkItemType"], workItem["Priority"]) - workItem["TimeCriticality"] = getTimeCriticality(workItem["WorkItemType"], workItem["TimeCriticality"]) - workItems.push(workItem); - } - workItems.sort(compareFunction); - return workItems; -} - -function initIndex(url) { - $.getJSON(url, { _: new Date().getTime() }, function (data) { - var workItems = getWorkItems(data); - console.log(data.length); - if (data.length > 0) - console.log(data[0]); - $("#HeaderGrid").igGrid({ - autoGenerateColumns: false, - dataSource: workItems, - height: "100%", - primaryKey: "Id", - width: "100%", - columns: [ - { key: "Id", dataType: "number" }, - { key: "Requester", dataType: "string" }, - { headerText: "Assigned To", key: "AssignedTo", dataType: "string" }, - { key: "Title", dataType: "string", width: "20%" }, - { headerText: "System(s)", key: "Tags", dataType: "string" }, - { key: "Priority", dataType: "string" }, - { headerText: "Qual/Eff", key: "TimeCriticality", dataType: "string" }, - { key: "State", dataType: "string" }, - { headerText: "Effort in Days", key: "Effort", dataType: "number" }, - { headerText: "UAT as of", key: "ResolvedDate", dataType: "date", format: "date" }, - { headerText: "CMP Date", key: "ClosedDate", dataType: "date", format: "date" }, - { headerText: "Target", key: "TargetDate", dataType: "date", format: "date" }, - { key: "AreaPath", dataType: "string", hidden: true }, - { key: "AssignedTo", dataType: "string", hidden: true }, - { key: "BusinessValue", dataType: "number", hidden: true }, - { key: "ChangedDate", dataType: "string", hidden: true }, - { key: "CommentCount", dataType: "number", hidden: true }, - { key: "CreatedDate", dataType: "string", hidden: true }, - { key: "Description", dataType: "string", hidden: true }, - { key: "IterationPath", dataType: "string", hidden: true }, - { key: "Revision", dataType: "number", hidden: true }, - { key: "RiskReductionMinusOpportunityEnablement", dataType: "string", hidden: true }, - { key: "StartDate", dataType: "string", hidden: true }, - { key: "WorkItemType", dataType: "string", hidden: true }, - { key: "WeightedShortestJobFirst", dataType: "number", hidden: true }, - ], - features: [ - { name: "Sorting", type: "local" }, - { name: "Filtering", type: "local" }, - { name: "Selection", mode: "row", multipleSelection: false, rowSelectionChanging: detailSelectionChangedRunInfo }, - { name: "Paging", type: "local", recordCountKey: "TotalRows", pageSize: 10, pageSizeUrlKey: "pageSize", "pageIndexUrlKey": "page", showPageSizeDropDown: true }, - ], - }); - }); - $("#HeaderGrid").on("dblclick", "tr", loadOne); -} \ No newline at end of file diff --git a/Static/js/mes.js b/Static/js/mes.js deleted file mode 100644 index 5395107..0000000 --- a/Static/js/mes.js +++ /dev/null @@ -1,161 +0,0 @@ -function compareFunction(a, b) { - return a.Priority[0] - b.Priority[0] || a.TimeCriticality[0] - b.TimeCriticality[0] || b.State[0] - a.State[0] || a.Id - b.Id; -} - -function showOne(rowData) { - if (rowData == null) - return; - var data = []; - data.push({ name: "Edit in ADO", value: 'Edit in ADO ' + rowData["Id"] + '' }); - for (const property in rowData) { - if (rowData[property] == null) - continue; - data.push({ name: property, value: rowData[property].toString() }); - } - $("#AllGrid").igGrid({ - autoGenerateColumns: true, - dataSource: data, - width: "100%", - showHeader: false, - }); -} - -function loadOne() { - var selectedRow = $("#HeaderGrid").data("igGridSelection").selectedRow(); - if (selectedRow == null) - return; - var rowData = $("#HeaderGrid").data("igGrid").dataSource.dataView()[selectedRow.index]; - showOne(rowData); -} - -function detailSelectionChangedRunInfo(evt, ui) { - if (ui.row.index === 0) - return; - var rowData = ui.owner.grid.dataSource.dataView()[ui.row.index]; - showOne(rowData); -} - -function getState(state) { - var result; - if (state == null) - result = "9-Null"; - else if (state === "New") - result = `1-${state}`; - else if (state === "Active") - result = `2-${state}`; - else if (state === "Resolved") - result = `3-${state}`; - else if (state === "Closed") - result = `4-${state}`; - else if (state === "Removed") - result = `5-${state}`; - else - result = `8-${state}`; - return result; -} - -function getPriority(workItemType, priority) { - var result; - if (workItemType === "Bug") - result = "0-Bug"; - else if (priority == null || priority === 0) - result = "9-Null"; - else if (priority === 1) - result = `${priority}-High`; - else if (priority === 2) - result = `${priority}-Med`; - else if (priority === 3) - result = `${priority}-Low`; - else if (priority === 4) - result = `${priority}-TBD`; - else - result = "8-Not"; - return result; -} - -function getTimeCriticality(workItemType, timeCriticality) { - var result; - if (workItemType === "Bug") - result = "0-Bug"; - else if (timeCriticality == null || timeCriticality === 0) - result = "9-Null"; - else if (timeCriticality === 1) - result = `${timeCriticality}-QSM`; - else if (timeCriticality === 2) - result = `${timeCriticality}-Qual`; - else if (timeCriticality === 3) - result = `${timeCriticality}-Eff`; - else - result = "8-Not"; - return result; -} - -function getWorkItems(data) { - var workItems = []; - var workItem; - for (var i = data.length - 1; i > -1; i--) { - workItem = data[i]; - if (workItem.AreaPath !== 'ART SPS\\MES') - continue; - if (workItem.Tags != null && workItem.Tags.includes("Ignore")) - continue; - if (workItem.WorkItemType !== 'Feature' && workItem.WorkItemType !== 'Bug') - continue; - workItem["State"] = getState(workItem["State"]) - workItem["Priority"] = getPriority(workItem["WorkItemType"], workItem["Priority"]) - workItem["TimeCriticality"] = getTimeCriticality(workItem["WorkItemType"], workItem["TimeCriticality"]) - workItems.push(workItem); - } - workItems.sort(compareFunction); - return workItems; -} - -function initIndex(url) { - $.getJSON(url, { _: new Date().getTime() }, function (data) { - var workItems = getWorkItems(data); - console.log(data.length); - if (data.length > 0) - console.log(data[0]); - $("#HeaderGrid").igGrid({ - autoGenerateColumns: false, - dataSource: workItems, - height: "100%", - primaryKey: "Id", - width: "100%", - columns: [ - { key: "Id", dataType: "number" }, - { key: "Requester", dataType: "string" }, - { headerText: "Assigned To", key: "AssignedTo", dataType: "string" }, - { key: "Title", dataType: "string", width: "20%" }, - { headerText: "System(s)", key: "Tags", dataType: "string" }, - { key: "Priority", dataType: "string" }, - { headerText: "Qual/Eff", key: "TimeCriticality", dataType: "string" }, - { key: "State", dataType: "string" }, - { headerText: "Effort in Days", key: "Effort", dataType: "number" }, - { headerText: "UAT as of", key: "ResolvedDate", dataType: "date", format: "date" }, - { headerText: "CMP Date", key: "ClosedDate", dataType: "date", format: "date" }, - { headerText: "Target", key: "TargetDate", dataType: "date", format: "date" }, - { key: "AreaPath", dataType: "string", hidden: true }, - { key: "AssignedTo", dataType: "string", hidden: true }, - { key: "BusinessValue", dataType: "number", hidden: true }, - { key: "ChangedDate", dataType: "string", hidden: true }, - { key: "CommentCount", dataType: "number", hidden: true }, - { key: "CreatedDate", dataType: "string", hidden: true }, - { key: "Description", dataType: "string", hidden: true }, - { key: "IterationPath", dataType: "string", hidden: true }, - { key: "Revision", dataType: "number", hidden: true }, - { key: "RiskReductionMinusOpportunityEnablement", dataType: "string", hidden: true }, - { key: "StartDate", dataType: "string", hidden: true }, - { key: "WorkItemType", dataType: "string", hidden: true }, - { key: "WeightedShortestJobFirst", dataType: "number", hidden: true }, - ], - features: [ - { name: "Sorting", type: "local" }, - { name: "Filtering", type: "local" }, - { name: "Selection", mode: "row", multipleSelection: false, rowSelectionChanging: detailSelectionChangedRunInfo }, - { name: "Paging", type: "local", recordCountKey: "TotalRows", pageSize: 10, pageSizeUrlKey: "pageSize", "pageIndexUrlKey": "page", showPageSizeDropDown: true }, - ], - }); - }); - $("#HeaderGrid").on("dblclick", "tr", loadOne); -} \ No newline at end of file diff --git a/Static/js/site-server.js b/Static/js/site-server.js index 3fb1840..6f5fd4a 100644 --- a/Static/js/site-server.js +++ b/Static/js/site-server.js @@ -2,12 +2,14 @@ var _chart = null; var _CdeId = null; var _apiUrl = null; var _BioRadId = null; +var _Collection = []; var _toolType = null; var _StaticUrl = null; var _workMaterial = {}; var _initialHeaderId = null; var _toolTypeMetaData = null; var _initialHeaderAttachmentId = null; +var _EcMesaFileShareCharacterizationSi = null; async function loadRunInfoAwaitingDisposition() { var row = $("#grid").igGrid("selectedRow"); @@ -169,6 +171,34 @@ function loadHeaderGridRunInfo() { }); } +function loadHeaderGridFiles() { + var toolTypeName = $("#ToolType").igCombo("text"); + $("#ToolTypeID").text(""); + hideDetailsDivRunInfo(); + disableHeaderButtonsRunInfo(); + $("#HeaderId").text(""); + $("#HeaderAttachmentId").text(""); + var gridCreated = $("#HeaderGrid").data("igGrid"); + if (gridCreated) + $("#HeaderGrid").igGrid("destroy"); + $.ajax({ + type: "GET", + url: _EcMesaFileShareCharacterizationSi + "/Archive/" + toolTypeName + ".json", + success: function (r) { + if ((r.Results == null) || (r.Results.ToolType == null) || (r.Results.Metadata == null)) + ShowErrorMessage("Invalid tool-type: " + toolTypeName); + else { + _toolType = r.Results.ToolType; + _toolTypeMetaData = r.Results.Metadata; + requestHeaderDataFiles(); + } + }, + error: function (e) { + DisplayWSMessage("error", "There was an error getting tool-type info by archive.", e); + } + }); +} + function disableHeaderButtonsRunInfo() { $("#GetDataButton").prop("disabled", true); $("#ReviewButton").prop("disabled", true); @@ -632,6 +662,39 @@ function copySelected(attachmentID, title, data) { copy(allText); } +function copySelectedB(attachmentID, title, collection) { + var allText = ""; + var headerText = ""; + var selectedRow = $("#HeaderGrid").data("igGridSelection").selectedRow(); + if (selectedRow !== null) { + var rowData = $("#HeaderGrid").data("igGrid").dataSource.dataView()[selectedRow.index]; + for (const property in rowData) { + if (property === "ID" || property === attachmentID || property === title) + continue; + allText = allText + property + '\t'; + headerText = headerText + rowData[property] + '\t'; + } + } + for (var i = 0; i < collection.length; i++) { + if (i === 0) { + for (const property in collection[i]) { + if (property === "ID" || property === "InsertDate" || property === attachmentID || property === title) + continue; + allText = allText + property + '\t'; + } + allText = allText + '\r'; + } + allText = allText + headerText; + for (const property in collection[i]) { + if (property === "ID" || property === "InsertDate" || property === attachmentID || property === title) + continue; + allText = allText + collection[i][property] + '\t'; + } + allText = allText + '\r'; + } + copy(allText); +} + function loadDetailsRunInfo() { showDetailsDivRunInfo(); loadHeaderAttachmentRunInfo(); @@ -667,7 +730,7 @@ function loadDetailsRunInfo() { } } $.getJSON(detailsURL, function (data) { - var gridParms = { + var gridParams = { autoGenerateColumns: false, primaryKey: "ID", features: [ @@ -682,21 +745,96 @@ function loadDetailsRunInfo() { dataBound: markAsReviewedRunInfo, }; if ((_toolType != null) && (_toolType.DataGridAttributes != null)) { - jQuery.extend(gridParms, JSON.parse(_toolType.DataGridAttributes)); + jQuery.extend(gridParams, JSON.parse(_toolType.DataGridAttributes)); } - $("#DetailsGrid").igGrid(gridParms); + $("#DetailsGrid").igGrid(gridParams); if ($("#chkCopyOnGet").is(':checked')) { copySelected(attachmentID, title, data); } }); } +function loadDetailsGridFiles() { + showDetailsDivRunInfo(); + loadHeaderAttachmentRunInfo(); + var collection = []; + var gridCreated = $("#DetailsGrid").data("igGrid"); + if (gridCreated) + $("#DetailsGrid").igGrid("destroy"); + var title = "Title"; + var attachmentID = "AttachmentID"; + var gridColumns = [ + { key: "ID", dataType: "number", hidden: true }, + { key: attachmentID, dataType: "string", hidden: true }, + { key: title, dataType: "string", hidden: true }, + ]; + var selectedRow = $("#HeaderGrid").data("igGridSelection").selectedRow(); + if (selectedRow == null) + return; + var rowData = $("#HeaderGrid").data("igGrid").dataSource.dataView()[selectedRow.index]; + for (var i = 0; i < _Collection.length; i++) { + if (_Collection[i].Details == null || _Collection[i].ID !== rowData.ID || _Collection[i].ArchiveLot !== rowData.ArchiveLot) + continue; + for (var j = 0; j < _Collection[i].Details.length; j++) { + _Collection[i].Details[j]['ID'] = j; + collection.push(_Collection[i].Details[j]); + } + } + if (collection.length === 0) { + gridColumns.push({ + key: "No Data", + headerText: "No Data", + width: "150px", + }); + } + else { + for (var i = 0; i < _toolTypeMetaData.length; i++) { + var f = _toolTypeMetaData[i]; + if ((f.Header == false) && (f.GridDisplayOrder > 0)) { + var col = { + key: f.ApiName, + headerText: f.DisplayTitle, + width: "150px", + }; + if (f.GridAttributes != null) + jQuery.extend(col, JSON.parse(f.GridAttributes)); + if (col.formatter != null) { + if (col.formatter == "boolToYesNo") + col.formatter = boolToYesNo; + else + col.formatter = null; + } + gridColumns.push(col); + } + } + } + var gridParams = { + autoGenerateColumns: false, + primaryKey: "ID", + features: [ + { name: "Selection", mode: "row" }, + { name: "Resizing" }, + { name: "Sorting", type: "local" } + ], + columns: gridColumns, + dataSource: collection, + dataSourceType: 'json' + }; + if ((_toolType != null) && (_toolType.DataGridAttributes != null)) { + jQuery.extend(gridParams, JSON.parse(_toolType.DataGridAttributes)); + } + $("#DetailsGrid").igGrid(gridParams); + if ($("#chkCopyOnGet").is(':checked')) { + copySelectedB(attachmentID, title, collection); + } +} + function requestHeaderDataRunInfo() { var startDate = $("#StartDate").igDatePicker("value"); var startTime = $("#StartTime").igTimePicker("value"); var endDate = $("#EndDate").igDatePicker("value"); var endTime = $("#EndTime").igTimePicker("value"); - var parms = { + var params = { datebegin: new Date( startDate.getFullYear(), startDate.getMonth(), startDate.getDate(), startTime.getHours(), startTime.getMinutes(), startTime.getSeconds()).toISOString(), @@ -707,7 +845,7 @@ function requestHeaderDataRunInfo() { var headerId = 0; if (_initialHeaderId > 0) { headerId = _initialHeaderId; - parms.headerid = headerId; + params.headerid = headerId; $("#HeaderId").text(headerId); $("#HeaderAttachmentId").text(_initialHeaderAttachmentId); _initialHeaderId = -1; @@ -716,7 +854,7 @@ function requestHeaderDataRunInfo() { $("#PinButton").hide(); else $("#PinButton").show(); - var headerURL = _apiUrl + "/tooltypes/" + _toolType.ID + "/headers?" + $.param(parms); + var headerURL = _apiUrl + "/tooltypes/" + _toolType.ID + "/headers?" + $.param(params); var gridColumns = [ { key: "ID", dataType: "number", hidden: true }, { key: "AttachmentID", dataType: "string", hidden: true }, @@ -741,7 +879,7 @@ function requestHeaderDataRunInfo() { gridColumns.push(col); } } - var gridParms = { + var gridParams = { autoGenerateColumns: false, primaryKey: "ID", height: "100%", @@ -758,14 +896,99 @@ function requestHeaderDataRunInfo() { responseDataKey: "Results", }; if ((_toolType != null) && (_toolType.HeaderGridAttributes != null)) { - jQuery.extend(gridParms, JSON.parse(_toolType.HeaderGridAttributes)); + jQuery.extend(gridParams, JSON.parse(_toolType.HeaderGridAttributes)); } - $("#HeaderGrid").igGrid(gridParms); + $("#HeaderGrid").igGrid(gridParams); if (headerId > 0) { loadDetailsRunInfo(); } } +function clearArray(array) { + if (array !== null) { + while (array.length > 0) { + array.pop(); + } + } +} + +function requestHeaderDataFiles() { + clearArray(_Collection); + var toolTypeName = $("#ToolType").igCombo("text"); + var startDate = $("#StartDate").igDatePicker("value"); + var startTime = $("#StartTime").igTimePicker("value"); + var endDate = $("#EndDate").igDatePicker("value"); + var endTime = $("#EndTime").igTimePicker("value"); + var params = { + area: null, + 'end-time': new Date( + endDate.getFullYear(), endDate.getMonth(), endDate.getDate(), + endTime.getHours(), endTime.getMinutes(), endTime.getSeconds()).toISOString(), + 'equipment-id': toolTypeName, + 'search-pattern': '*.wc', + 'start-time': new Date( + startDate.getFullYear(), startDate.getMonth(), startDate.getDate(), + startTime.getHours(), startTime.getMinutes(), startTime.getSeconds()).toISOString(), + 'wafer-size': null, + } + var headerURL = _apiUrl + "/v1/file-share/archive-data/?" + $.param(params); + var gridColumns = [ + { key: "ID", dataType: "number", hidden: true }, + { key: "ArchiveLot", dataType: "string", hidden: true }, + { key: "ArchiveLastWriteTime", dataType: "date", hidden: true }, + { key: "AttachmentID", dataType: "string", hidden: true }, + { key: "Title", dataType: "string", hidden: true }, + ]; + for (var i = 0; i < _toolTypeMetaData.length; i++) { + var f = _toolTypeMetaData[i]; + if ((f.Header == true) && (f.GridDisplayOrder > 0)) { + var col = { + key: f.ApiName, + headerText: f.DisplayTitle, + width: "150px", + }; + if (f.GridAttributes != null) + jQuery.extend(col, JSON.parse(f.GridAttributes)); + if (col.formatter != null) { + if (col.formatter == "boolToYesNo") + col.formatter = boolToYesNo; + else + col.formatter = null; + } + gridColumns.push(col); + } + } + $.getJSON(headerURL, function (data) { + var singleton; + for (var i = 0; i < data.length; i++) { + singleton = JSON.parse(data[i].lines.join(' ')); + singleton['ID'] = i; + singleton['ArchiveLot'] = data[i].lot; + singleton['ArchiveLastWriteTime'] = data[i].lastWriteTime; + _Collection.push(singleton); + } + var gridParams = { + autoGenerateColumns: false, + height: "100%", + width: "100%", + features: [ + { name: "Paging", type: "local", recordCountKey: "TotalRows", pageSize: 100, pageSizeList: [50, 100, 250, 500], pageSizeUrlKey: "pageSize", "pageIndexUrlKey": "page" }, + { name: "Selection", mode: "row", rowSelectionChanged: headerSelectionChangedRunInfo }, + { name: "Filtering", type: "local" }, + { name: 'Resizing' }, + { name: "Sorting", type: "local" } + ], + columns: gridColumns, + dataSource: _Collection, + responseDataKey: "Results", + }; + if ((_toolType != null) && (_toolType.HeaderGridAttributes != null)) { + jQuery.extend(gridParams, JSON.parse(_toolType.HeaderGridAttributes)); + } + $("#HeaderGrid").igGrid(gridParams); + }); +} + function reviewButtonRunInfo() { var toolTypeId = $("#ToolTypeID").text(); var headerId = parseInt($("#HeaderId").text()); @@ -1274,43 +1497,29 @@ function restartButton() { clearWorkMaterial(); }; -function initFiles(apiUrl, staticUrl, initialToolTypeID, initialHeaderId, initialHeaderAttachmentId) { +function initFiles(apiUrl, staticUrl, ecMesaFileShareCharacterizationSi) { _apiUrl = apiUrl; + _initialHeaderId = "" _StaticUrl = staticUrl; - _initialHeaderId = initialHeaderId === null ? "" : initialHeaderId; - _initialHeaderAttachmentId = initialHeaderAttachmentId === null ? "" : initialHeaderAttachmentId; - $.getJSON(_apiUrl + '/tooltypes', function (data) { - for (var i = 0; i < data.Results.length; i++) { - if (data.Results[i].ToolTypeName === "CDE") { - _CdeId = data.Results[i].ID; - } - else if (data.Results[i].ToolTypeName === "BioRad") { - _BioRadId = data.Results[i].ID; - } - } + _initialHeaderAttachmentId = ""; + _EcMesaFileShareCharacterizationSi = ecMesaFileShareCharacterizationSi; + $.getJSON(_apiUrl + '/v1/file-share/equipment-ids', function (data) { $("#ToolType").igCombo({ dataSource: data, - responseDataKey: "Results", - textKey: "ToolTypeName", - valueKey: "ID", + textKey: "toolTypeName", + valueKey: "id", mode: "dropdown", width: 150, - itemsRendered: function (evt, ui) { - loadHeaderGridRunInfo(); + itemsRendered: function () { + loadHeaderGridFiles(); }, - selectionChanged: loadHeaderGridRunInfo, - initialSelectedItems: [{ value: initialToolTypeID === null ? 1 : initialToolTypeID }] + selectionChanged: loadHeaderGridFiles, }); }); setInitialDateTimesRunInfo(6 * 60 * 60 * 1000); - $("#HeaderGrid").on("dblclick", "tr", loadDetailsRunInfo); - $("#LoadHeadersButton").click(loadHeaderGridRunInfo); - $("#GetDataButton").click(loadDetailsRunInfo); - $("#ReviewButton").click(reviewButtonRunInfo); - $("#RecipeParametersButton").click(recipeParametersButtonRunInfo); - $("#ViewButton").click(viewButtonRunInfo); - $("#PinButton").click(pinButtonRunInfo); - $("#OIExportButton").click(oiExportButtonRunInfo); + $("#HeaderGrid").on("dblclick", "tr", loadDetailsGridFiles); + $("#LoadHeadersButton").click(loadHeaderGridFiles); + $("#GetDataButton").click(loadDetailsGridFiles); setInterval(function () { if ($("#chkAutoRefresh").is(':checked')) { setInitialDateTimesRunInfo(null); diff --git a/Static/leo.html b/Static/leo.html deleted file mode 100644 index f5ccc2e..0000000 --- a/Static/leo.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - FI Backlog HiRel (Leominster) - - - - - - - - - - - - -

FI Backlog HiRel (Leominster)

- -
-
-
- -
  - -
-
-
- - - - - diff --git a/Static/mes.html b/Static/mes.html deleted file mode 100644 index db44d7b..0000000 --- a/Static/mes.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - FI Backlog Mesa - - - - - - - - - - - - -

FI Backlog Mesa

- -
-
-
- -
  - -
-
-
- - - - - diff --git a/Static/styles/files.css b/Static/styles/files.css new file mode 100644 index 0000000..1aa12db --- /dev/null +++ b/Static/styles/files.css @@ -0,0 +1,12 @@ +#HeaderGridDiv, +#DetailsGridDiv { + font-size: 12px; +} + +#HeaderGrid { + font-family: monospace; +} + +#DetailsGrid { + font-family: monospace; +} \ No newline at end of file diff --git a/Tests/OI.Metrology.Tests.csproj b/Tests/OI.Metrology.Tests.csproj index 800d62a..70dfee7 100644 --- a/Tests/OI.Metrology.Tests.csproj +++ b/Tests/OI.Metrology.Tests.csproj @@ -26,15 +26,15 @@ Linux - - - - - - - - - + + + + + + + + + diff --git a/Tests/UnitTestFileShareController.cs b/Tests/UnitTestFileShareController.cs index 5a758f4..5b575c9 100644 --- a/Tests/UnitTestFileShareController.cs +++ b/Tests/UnitTestFileShareController.cs @@ -1,8 +1,10 @@ using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using OI.Metrology.Shared.DataModels; using OI.Metrology.Shared.Models; using OI.Metrology.Shared.Models.Stateless; +using System.Collections.ObjectModel; namespace OI.Metrology.Tests; @@ -88,7 +90,7 @@ public class UnitTestFileShareController public void GetArchiveData() { _Logger?.LogInformation("Starting Web Application"); - List? result; + ReadOnlyCollection? result; CharacterizationParameters characterizationParameters; IServiceProvider? serviceProvider = _WebApplicationFactory?.Services.CreateScope().ServiceProvider; IFileShareRepository? fileShareRepository = serviceProvider?.GetRequiredService(); @@ -117,4 +119,28 @@ public class UnitTestFileShareController NonThrowTryCatch(); } + [TestMethod] + public void EquipmentIds() + { + _Logger?.LogInformation("Starting Web Application"); + IServiceProvider? serviceProvider = _WebApplicationFactory?.Services.CreateScope().ServiceProvider; + IFileShareRepository? fileShareRepository = serviceProvider?.GetRequiredService(); + ReadOnlyCollection? result = fileShareRepository?.GetEquipmentIds(); + Assert.IsNotNull(result); + _Logger?.LogInformation("{TestName} completed", _TestContext?.TestName); + NonThrowTryCatch(); + } + + [TestMethod] + public async Task EquipmentIdsApi() + { + HttpClient? httpClient = _WebApplicationFactory?.CreateClient(); + _Logger?.LogInformation("Starting Web Application"); + Assert.IsTrue(httpClient is not null); + string? response = await httpClient.GetStringAsync($"api/v1/file-share/equipment-ids"); + Assert.IsNotNull(response); + _Logger?.LogInformation("{TestName} completed", _TestContext?.TestName); + NonThrowTryCatch(); + } + } \ No newline at end of file diff --git a/Wafer-Counter/.vscode/tasks.json b/Wafer-Counter/.vscode/tasks.json index 6bcb3a2..810ef90 100644 --- a/Wafer-Counter/.vscode/tasks.json +++ b/Wafer-Counter/.vscode/tasks.json @@ -1,6 +1,16 @@ { "version": "2.0.0", "tasks": [ + { + "label": "dotnetRunDebug", + "command": "dotnet OI.Metrology.Wafer.Counter.dll", + "dependsOn": "build", + "problemMatcher": [], + "type": "shell", + "options": { + "cwd": "${workspaceFolder}/bin/Debug/net8.0" + } + }, { "label": "build", "command": "dotnet", diff --git a/Wafer-Counter/ApiControllers/AzureDevOpsController.cs b/Wafer-Counter/ApiControllers/AzureDevOpsController.cs new file mode 100644 index 0000000..814ac6f --- /dev/null +++ b/Wafer-Counter/ApiControllers/AzureDevOpsController.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Mvc; +using OI.Metrology.Shared.Models; +using OI.Metrology.Shared.Models.Stateless; +using OI.Metrology.Wafer.Counter.Helper; + +namespace OI.Metrology.Wafer.Counter.ApiControllers; + +[Route("api/v1/ado")] +public class AzureDevOpsController : Controller, IAzureDevOpsController +{ + + private readonly IAzureDevOpsRepository _AzureDevOpsRepository; + + public AzureDevOpsController(IAzureDevOpsRepository azureDevOpsRepository) => + _AzureDevOpsRepository = azureDevOpsRepository; + + [HttpPost("save")] + public IResult Save() + { + PollValue? pollValue = ParameterHelper.GetPollValue(Request.HttpContext.Connection?.RemoteIpAddress, Request.Body); + ArgumentNullException.ThrowIfNull(pollValue); + _AzureDevOpsRepository.Save(pollValue); + return Results.Ok(); + } + +} \ No newline at end of file diff --git a/Wafer-Counter/ApiControllers/FileShareController.cs b/Wafer-Counter/ApiControllers/FileShareController.cs index e16c2cf..c47bd02 100644 --- a/Wafer-Counter/ApiControllers/FileShareController.cs +++ b/Wafer-Counter/ApiControllers/FileShareController.cs @@ -1,9 +1,9 @@ using Microsoft.AspNetCore.Mvc; +using OI.Metrology.Shared.DataModels; using OI.Metrology.Shared.Models; using OI.Metrology.Shared.Models.Stateless; -using System.Collections.Specialized; -using System.Text.Json; -using System.Web; +using OI.Metrology.Wafer.Counter.Helper; +using System.Collections.ObjectModel; namespace OI.Metrology.Wafer.Counter.ApiControllers; @@ -37,39 +37,21 @@ public class FileShareController : Controller, IFileShareController return Results.Ok(); } - private static Dictionary GetKeyValuePairs(QueryString queryString) - { - Dictionary results = []; - if (queryString.HasValue) - { - NameValueCollection nameValueCollection = HttpUtility.ParseQueryString(queryString.Value); - foreach (string? key in nameValueCollection.AllKeys) - { - if (key is null) - continue; - results.Add(key, nameValueCollection[key]); - } - } - return results; - } - - private static CharacterizationParameters? GetCharacterizationParameters(QueryString queryString) - { - CharacterizationParameters? result; - Dictionary keyValuePairs = GetKeyValuePairs(queryString); - string json = JsonSerializer.Serialize(keyValuePairs); - result = string.IsNullOrEmpty(json) ? null : JsonSerializer.Deserialize(json, CharacterizationParametersSourceGenerationContext.Default.CharacterizationParameters); - return result; - } - [HttpGet("archive-data")] public IActionResult ArchiveData() { - List results; - CharacterizationParameters? characterizationParameters = GetCharacterizationParameters(Request.QueryString); + ReadOnlyCollection results; + CharacterizationParameters? characterizationParameters = ParameterHelper.GetCharacterizationParameters(Request.QueryString); ArgumentNullException.ThrowIfNull(characterizationParameters); results = _FileShareRepository.GetArchiveData(characterizationParameters); return Json(results); } + [HttpGet("equipment-ids")] + public IActionResult EquipmentIds() + { + ReadOnlyCollection results = _FileShareRepository.GetEquipmentIds(); + return Json(results); + } + } \ No newline at end of file diff --git a/Wafer-Counter/Helper/ParameterHelper.cs b/Wafer-Counter/Helper/ParameterHelper.cs new file mode 100644 index 0000000..38e7b5b --- /dev/null +++ b/Wafer-Counter/Helper/ParameterHelper.cs @@ -0,0 +1,82 @@ +using OI.Metrology.Shared.Models; +using System.Collections.Specialized; +using System.Net; +using System.Text.Json; +using System.Web; + +namespace OI.Metrology.Wafer.Counter.Helper; + +public class ParameterHelper +{ + + private static Dictionary GetKeyValuePairs(QueryString queryString) + { + Dictionary results = []; + if (queryString.HasValue) + { + NameValueCollection nameValueCollection = HttpUtility.ParseQueryString(queryString.Value); + foreach (string? key in nameValueCollection.AllKeys) + { + if (key is null) + continue; + results.Add(key, nameValueCollection[key]); + } + } + return results; + } + + internal static CharacterizationParameters? GetCharacterizationParameters(QueryString queryString) + { + CharacterizationParameters? result; + Dictionary keyValuePairs = GetKeyValuePairs(queryString); + string json = JsonSerializer.Serialize(keyValuePairs, new JsonSerializerOptions() { WriteIndented = true }); + result = string.IsNullOrEmpty(json) ? null : JsonSerializer.Deserialize(json, CharacterizationParametersSourceGenerationContext.Default.CharacterizationParameters); + return result; + } + + private static string? GetQueryString(Stream stream) + { + string? result; + if (!stream.CanRead) + result = null; + else + { + Task task = new StreamReader(stream).ReadToEndAsync(); + result = task.Result; + } + return result; + } + + private static Dictionary GetKeyValuePairs(string? queryString) + { + Dictionary results = []; + if (!string.IsNullOrEmpty(queryString)) + { + NameValueCollection nameValueCollection = HttpUtility.ParseQueryString(queryString); + foreach (string? key in nameValueCollection.AllKeys) + { + if (key is null) + continue; + results.Add(key, nameValueCollection[key]); + } + } + return results; + } + + internal static PollValue? GetPollValue(IPAddress? remoteIpAddress, Stream stream) + { + PollValue? result; + string? queryString = GetQueryString(stream); + Dictionary keyValuePairs = GetKeyValuePairs(queryString); + string json = JsonSerializer.Serialize(keyValuePairs, new JsonSerializerOptions() { WriteIndented = true }); + result = string.IsNullOrEmpty(json) ? null : JsonSerializer.Deserialize(json, PollValueSourceGenerationContext.Default.PollValue); + if (result is not null) + { + result = new(null, result.Id, result.Page, queryString, remoteIpAddress is null ? string.Empty : remoteIpAddress.ToString(), result.Time, result.Value); + json = JsonSerializer.Serialize(result, PollValueSourceGenerationContext.Default.PollValue); + result = new(json, result.Id, result.Page, queryString, remoteIpAddress is null ? string.Empty : remoteIpAddress.ToString(), result.Time, result.Value); + } + return result; + } + +} \ No newline at end of file diff --git a/Wafer-Counter/Repositories/RegexHelper.cs b/Wafer-Counter/Helper/RegexHelper.cs similarity index 78% rename from Wafer-Counter/Repositories/RegexHelper.cs rename to Wafer-Counter/Helper/RegexHelper.cs index 2067a20..5311627 100644 --- a/Wafer-Counter/Repositories/RegexHelper.cs +++ b/Wafer-Counter/Helper/RegexHelper.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; -namespace OI.Metrology.Wafer.Counter.Repository; +namespace OI.Metrology.Wafer.Counter.Helper; public partial class RegexHelper { @@ -8,4 +8,4 @@ public partial class RegexHelper [GeneratedRegex(@"[\\,\/,\:,\*,\?,\"",\<,\>,\|]")] internal static partial Regex WindowsFileSystem(); -} +} \ No newline at end of file diff --git a/Wafer-Counter/Models/AppSettings.cs b/Wafer-Counter/Models/AppSettings.cs index 20ed81f..3b54fb0 100644 --- a/Wafer-Counter/Models/AppSettings.cs +++ b/Wafer-Counter/Models/AppSettings.cs @@ -1,8 +1,10 @@ using System.Text.Json; +using System.Text.Json.Serialization; namespace OI.Metrology.Wafer.Counter.Models; -public record AppSettings(string BuildNumber, +public record AppSettings(string AzureDevOpsDestinationDirectory, + string BuildNumber, string Company, string EcCharacterizationSi, string EcMesaFileShareCharacterizationSi, @@ -20,8 +22,14 @@ public record AppSettings(string BuildNumber, public override string ToString() { - string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings); return result; } +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(AppSettings))] +public partial class AppSettingsSourceGenerationContext : JsonSerializerContext +{ } \ No newline at end of file diff --git a/Wafer-Counter/Models/Binder/AppSettings.cs b/Wafer-Counter/Models/Binder/AppSettings.cs index 586f742..d032b9c 100644 --- a/Wafer-Counter/Models/Binder/AppSettings.cs +++ b/Wafer-Counter/Models/Binder/AppSettings.cs @@ -6,6 +6,7 @@ namespace OI.Metrology.Wafer.Counter.Models.Binder; public class AppSettings { + public string? AzureDevOpsDestinationDirectory { get; set; } public string? BuildNumber { get; set; } public string? Company { get; set; } public string? EcCharacterizationSi { get; set; } @@ -23,7 +24,7 @@ public class AppSettings public override string ToString() { - string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + string result = JsonSerializer.Serialize(this, BinderAppSettingsSourceGenerationContext.Default.AppSettings); return result; } @@ -48,6 +49,7 @@ public class AppSettings { Models.AppSettings result; if (appSettings is null) throw new NullReferenceException(nameof(appSettings)); + if (appSettings.AzureDevOpsDestinationDirectory is null) throw new NullReferenceException(nameof(AzureDevOpsDestinationDirectory)); if (appSettings.BuildNumber is null) throw new NullReferenceException(nameof(BuildNumber)); if (appSettings.Company is null) throw new NullReferenceException(nameof(Company)); if (appSettings.EcCharacterizationSi is null) throw new NullReferenceException(nameof(EcCharacterizationSi)); @@ -62,7 +64,8 @@ public class AppSettings if (appSettings.WaferCounterDestinationDirectory is null) throw new NullReferenceException(nameof(WaferCounterDestinationDirectory)); if (appSettings.WaferCounterTwoFileSecondsWait is null) throw new NullReferenceException(nameof(WaferCounterTwoFileSecondsWait)); if (appSettings.WorkingDirectoryName is null) throw new NullReferenceException(nameof(WorkingDirectoryName)); - result = new(appSettings.BuildNumber, + result = new(appSettings.AzureDevOpsDestinationDirectory, + appSettings.BuildNumber, appSettings.Company, appSettings.EcCharacterizationSi, appSettings.EcMesaFileShareCharacterizationSi, diff --git a/Wafer-Counter/OI.Metrology.Wafer.Counter.csproj b/Wafer-Counter/OI.Metrology.Wafer.Counter.csproj index 36944cc..fe6ac58 100644 --- a/Wafer-Counter/OI.Metrology.Wafer.Counter.csproj +++ b/Wafer-Counter/OI.Metrology.Wafer.Counter.csproj @@ -19,17 +19,17 @@ - - - - - - - - - + + + + + + + + + - + diff --git a/Wafer-Counter/Program.cs b/Wafer-Counter/Program.cs index 539e865..1b33cdd 100644 --- a/Wafer-Counter/Program.cs +++ b/Wafer-Counter/Program.cs @@ -31,6 +31,7 @@ public class Program _ = webApplicationBuilder.Services.AddHttpClient(); _ = webApplicationBuilder.Services.AddSingleton(_ => appSettings); _ = webApplicationBuilder.Services.AddSingleton(); + _ = webApplicationBuilder.Services.AddSingleton(); _ = webApplicationBuilder.Services.AddSingleton(); _ = webApplicationBuilder.Services.AddSingleton>(_ => appSettingsRepository); diff --git a/Wafer-Counter/Repositories/AzureDevOpsRepository.cs b/Wafer-Counter/Repositories/AzureDevOpsRepository.cs new file mode 100644 index 0000000..cf4a597 --- /dev/null +++ b/Wafer-Counter/Repositories/AzureDevOpsRepository.cs @@ -0,0 +1,25 @@ +using OI.Metrology.Shared.Models; +using OI.Metrology.Shared.Models.Stateless; +using OI.Metrology.Wafer.Counter.Models; + +namespace OI.Metrology.Wafer.Counter.Repository; + +public class AzureDevOpsRepository : IAzureDevOpsRepository +{ + + private readonly AppSettings _AppSettings; + + public AzureDevOpsRepository(AppSettings appSettings) => + _AppSettings = appSettings; + + void IAzureDevOpsRepository.Save(PollValue pollValue) + { + ArgumentNullException.ThrowIfNull(pollValue.Id); + ArgumentNullException.ThrowIfNull(pollValue.Page); + string directory = Path.Combine(_AppSettings.AzureDevOpsDestinationDirectory, pollValue.Page, pollValue.Id.Value.ToString()); + if (!Directory.Exists(directory)) + _ = Directory.CreateDirectory(directory); + File.WriteAllText(Path.Combine(directory, $"{pollValue.Time}.json"), pollValue.Json is null ? string.Empty : pollValue.Json); + } + +} \ No newline at end of file diff --git a/Wafer-Counter/Repositories/FileShareRepository.cs b/Wafer-Counter/Repositories/FileShareRepository.cs index 9972634..68ab648 100644 --- a/Wafer-Counter/Repositories/FileShareRepository.cs +++ b/Wafer-Counter/Repositories/FileShareRepository.cs @@ -1,3 +1,4 @@ +using OI.Metrology.Shared.DataModels; using OI.Metrology.Shared.Models; using OI.Metrology.Shared.Models.Stateless; using OI.Metrology.Wafer.Counter.Models; @@ -82,7 +83,7 @@ public class FileShareRepository : IFileShareRepository return result; } - List IFileShareRepository.GetNginxFileSystemSortableCollection(HttpClient httpClient, Uri uri, string? endsWith) + ReadOnlyCollection IFileShareRepository.GetNginxFileSystemSortableCollection(HttpClient httpClient, Uri uri, string? endsWith) { List results = new(); Task httpResponseMessage = httpClient.GetAsync(uri); @@ -101,12 +102,12 @@ public class FileShareRepository : IFileShareRepository results.Add(nginxFileSystemSortable); } } - return results; + return new(results); } private static ReadOnlyCollection GetValidDirectories(string equipmentDirectory, DateTime startDateTime, DateTime endDateTime) { - List results = [equipmentDirectory]; + List results = []; DateTime dateTime; string weekOfYear; Calendar calendar = new CultureInfo("en-US").Calendar; @@ -121,27 +122,37 @@ public class FileShareRepository : IFileShareRepository return new(results); } - private static ReadOnlyCollection GetCollection(CharacterizationParameters characterizationParameters, string searchPattern, DateTime startDateTime, DateTime endDateTime, ReadOnlyCollection validDirectories) + private static ReadOnlyCollection GetFiles(CharacterizationParameters characterizationParameters, string equipmentDirectory, string searchPattern, DateTime startDateTime, DateTime endDateTime, ReadOnlyCollection validDirectories) { - FileInfo[] results; + List results = []; string[] directories; - List collection = []; string startDateTimeTicks = startDateTime.Ticks.ToString(); string delta = (endDateTime.Ticks - startDateTime.Ticks).ToString(); - string ticksSearchPattern = $"{startDateTime.Ticks.ToString()[..(startDateTimeTicks.Length - delta.Length + 1)]}*"; + string ticksSearchPattern = $"{startDateTime.Ticks.ToString()[..(startDateTimeTicks.Length - delta.Length - 1)]}*"; + bool check = characterizationParameters.SearchPattern is null || searchPattern == characterizationParameters.SearchPattern; + if (check) + results.AddRange(Directory.GetFiles(equipmentDirectory, searchPattern, SearchOption.AllDirectories)); foreach (string validDirectory in validDirectories) { if (string.IsNullOrEmpty(validDirectory) || !Directory.Exists(validDirectory)) continue; - if (characterizationParameters.SearchPattern is null || searchPattern == characterizationParameters.SearchPattern) - collection.AddRange(Directory.GetFiles(validDirectory, searchPattern, SearchOption.AllDirectories).Select(l => new FileInfo(l))); + if (check) + results.AddRange(Directory.GetFiles(validDirectory, searchPattern, SearchOption.AllDirectories)); else { directories = Directory.GetDirectories(validDirectory, ticksSearchPattern, SearchOption.AllDirectories); foreach (string directory in directories) - collection.AddRange(Directory.GetFiles(directory, searchPattern, SearchOption.TopDirectoryOnly).Select(l => new FileInfo(l))); + results.AddRange(Directory.GetFiles(directory, searchPattern, SearchOption.TopDirectoryOnly)); } } + return new(results); + } + + private static ReadOnlyCollection GetCollection(CharacterizationParameters characterizationParameters, string equipmentDirectory, string searchPattern, DateTime startDateTime, DateTime endDateTime, ReadOnlyCollection validDirectories) + { + FileInfo[] results; + ReadOnlyCollection files = GetFiles(characterizationParameters, equipmentDirectory, searchPattern, startDateTime, endDateTime, validDirectories); + FileInfo[] collection = files.Select(l => new FileInfo(l)).ToArray(); results = (from l in collection where l.LastWriteTime >= startDateTime && l.LastWriteTime <= endDateTime orderby l.LastWriteTime descending select l).ToArray(); return new(results); } @@ -193,7 +204,7 @@ public class FileShareRepository : IFileShareRepository DateTime endDateTime = characterizationParameters.EndTime is null ? DateTime.Now : DateTime.Parse(characterizationParameters.EndTime).ToLocalTime(); DateTime startDateTime = characterizationParameters.StartTime is null ? DateTime.Now.AddHours(-6) : DateTime.Parse(characterizationParameters.StartTime).ToLocalTime(); ReadOnlyCollection validDirectories = GetValidDirectories(equipmentDirectory, startDateTime, endDateTime); - ReadOnlyCollection collection = GetCollection(characterizationParameters, searchPattern, startDateTime, endDateTime, validDirectories); + ReadOnlyCollection collection = GetCollection(characterizationParameters, equipmentDirectory, searchPattern, startDateTime, endDateTime, validDirectories); foreach (FileInfo fileInfo in collection) { if (string.IsNullOrEmpty(fileInfo.DirectoryName)) @@ -218,7 +229,7 @@ public class FileShareRepository : IFileShareRepository return new(results); } - List IFileShareRepository.GetArchiveData(CharacterizationParameters characterizationParameters) + ReadOnlyCollection IFileShareRepository.GetArchiveData(CharacterizationParameters characterizationParameters) { List results = []; string searchPattern; @@ -240,4 +251,23 @@ public class FileShareRepository : IFileShareRepository return new(results); } + ReadOnlyCollection IFileShareRepository.GetEquipmentIds() + { + List results = []; + string directoryName; + ToolTypeNameId toolTypeNameId; + string archiveDirectory = Path.Combine(_AppSettings.EcCharacterizationSi, "Archive"); + string[] directories = Directory.GetDirectories(archiveDirectory, "*", SearchOption.TopDirectoryOnly); + string[] fileNames = Directory.GetFiles(archiveDirectory, "*.json", SearchOption.TopDirectoryOnly).Select(l => Path.GetFileNameWithoutExtension(l)).ToArray(); + for (int i = 0; i < directories.Length; i++) + { + directoryName = Path.GetFileName(directories[i]); + if (!fileNames.Contains(directoryName)) + continue; + toolTypeNameId = new() { ID = i, ToolTypeName = directoryName }; + results.Add(toolTypeNameId); + } + return new(results); + } + } \ No newline at end of file diff --git a/Wafer-Counter/Repositories/WaferCounterRepository.cs b/Wafer-Counter/Repositories/WaferCounterRepository.cs index e358c13..ba5f62f 100644 --- a/Wafer-Counter/Repositories/WaferCounterRepository.cs +++ b/Wafer-Counter/Repositories/WaferCounterRepository.cs @@ -1,7 +1,9 @@ using OI.Metrology.Shared.DataModels; using OI.Metrology.Shared.Models; using OI.Metrology.Shared.Models.Stateless; +using OI.Metrology.Wafer.Counter.Helper; using OI.Metrology.Wafer.Counter.Models; +using System.Collections.ObjectModel; using System.Globalization; using System.Text.Json; using System.Text.RegularExpressions; @@ -32,17 +34,25 @@ public class WaferCounterRepository : IWaferCounterRepository _RepositoryName = nameof(WaferCounterRepository)[..^10]; } - private void MoveFile(string area, string waferSize, string windowsFileSystemSafeText, string waferSizeDirectory, NginxFileSystemSortable nginxFileSystemSortable) + private void MoveFile(string area, string waferSize, WaferCounter? waferCounter, string windowsFileSystemSafeText, string waferSizeDirectory, NginxFileSystemSortable nginxFileSystemSortable) { string equipmentId = $"{area}-{waferSize}"; + WaferCounterArchive waferCounterArchive = new() + { + Date = nginxFileSystemSortable.DateTime, + MesEntity = equipmentId, + RDS = windowsFileSystemSafeText, + SlotMap = waferCounter?.SlotMap, + Text = waferCounter?.Text, + Total = waferCounter?.Total, + }; Calendar calendar = new CultureInfo("en-US").Calendar; string from = Path.Combine(waferSizeDirectory, nginxFileSystemSortable.Name); string archive = Path.Combine(_AppSettings.EcCharacterizationSi, "Archive", equipmentId); - HeaderCommon headerCommon = new() { RDS = windowsFileSystemSafeText, MesEntity = equipmentId }; string weekOfYear = $"{nginxFileSystemSortable.DateTime:yyyy}_Week_{calendar.GetWeekOfYear(nginxFileSystemSortable.DateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}"; string directory = Path.Combine(archive, weekOfYear, nginxFileSystemSortable.DateTime.ToString("yyyy-MM-dd"), windowsFileSystemSafeText); string file = Path.Combine(directory, nginxFileSystemSortable.DateTime.Ticks.ToString(), $"{nginxFileSystemSortable.Name}.json"); - string json = JsonSerializer.Serialize(headerCommon, new JsonSerializerOptions() { WriteIndented = true }); + string json = JsonSerializer.Serialize(waferCounterArchive, WaferCounterArchiveSourceGenerationContext.Default.WaferCounterArchive); _FileShareRepository.FileWrite(file, json); string to = Path.Combine(directory, nginxFileSystemSortable.Name); _FileShareRepository.MoveFile(from, to); @@ -145,10 +155,12 @@ public class WaferCounterRepository : IWaferCounterRepository { List results = new(); DateTime dateTime = DateTime.Now; + ReadOnlyCollection collection; long ticks = dateTime.AddSeconds(_AppSettings.WaferCounterTwoFileSecondsWait).Ticks; for (int i = 0; i < int.MaxValue; i++) { - results = _FileShareRepository.GetNginxFileSystemSortableCollection(httpClient, waferSizeUri, ".wc"); + collection = _FileShareRepository.GetNginxFileSystemSortableCollection(httpClient, waferSizeUri, ".wc"); + results.AddRange(collection); if (results.Count > 0 || DateTime.Now.Ticks > ticks) break; Thread.Sleep(250); @@ -197,7 +209,7 @@ public class WaferCounterRepository : IWaferCounterRepository string windowsFileSystemSafeText = _Regex.Replace(text, "."); result = GetLastQuantityAndSlotMap(waferSize, httpClient, nginxFileSystemSortableCollection[0]); for (int i = 0; i < nginxFileSystemSortableCollection.Count; i++) - MoveFile(area, waferSize, windowsFileSystemSafeText, waferSizeDirectory, nginxFileSystemSortableCollection[i]); + MoveFile(area, waferSize, result, windowsFileSystemSafeText, waferSizeDirectory, nginxFileSystemSortableCollection[i]); } return result; }