diff --git a/Server/.vscode/tasks.json b/Server/.vscode/tasks.json index 57c5d6d..90e682c 100644 --- a/Server/.vscode/tasks.json +++ b/Server/.vscode/tasks.json @@ -66,6 +66,16 @@ ], "problemMatcher": "$msCompile" }, + { + "label": "Format-Whitespace", + "command": "dotnet", + "type": "process", + "args": [ + "format", + "whitespace" + ], + "problemMatcher": "$msCompile" + }, { "label": "publish", "command": "dotnet", @@ -85,6 +95,24 @@ ], "problemMatcher": "$msCompile" }, + { + "label": "Publish AOT", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/OI.Metrology.Server.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary", + "--configuration", + "Release", + "--runtime", + "win-x64", + "-p:PublishAot=true", + "/property:GenerateFullPaths=true" + ], + "problemMatcher": "$msCompile" + }, { "label": "old-watch", "command": "dotnet", diff --git a/Server/ApiControllers/FileShareController.cs b/Server/ApiControllers/FileShareController.cs index 9e535c3..06cac86 100644 --- a/Server/ApiControllers/FileShareController.cs +++ b/Server/ApiControllers/FileShareController.cs @@ -22,7 +22,7 @@ public class FileShareController : Controller, IFileShareController [HttpGet("move-file")] public IResult MoveFile(string from, string to) { - _FileShareRepository.CopyFile(from, to); + _FileShareRepository.MoveFile(from, to); return Results.Ok(); } diff --git a/Server/ApiControllers/InboundController.cs b/Server/ApiControllers/InboundController.cs index bff439f..b206210 100644 --- a/Server/ApiControllers/InboundController.cs +++ b/Server/ApiControllers/InboundController.cs @@ -4,6 +4,7 @@ using OI.Metrology.Shared.Models; using OI.Metrology.Shared.Models.Stateless; using OI.Metrology.Shared.Services; using System.Net; +using System.Text.Json; namespace OI.Metrology.Server.ApiControllers; @@ -29,22 +30,9 @@ public partial class InboundController : ControllerBase, IInboundController task = new StreamReader(stream).ReadToEndAsync(); - result = task.Result; - } - return result; - } - [HttpPost] [Route("{tooltype}")] - public IActionResult Post(string tooltype) + public IActionResult Post(string tooltype, JsonElement? jsonElement) { IPAddress? remoteIP = HttpContext.Connection.RemoteIpAddress; if (!_InboundRepository.IsIPAddressAllowed(_AppSettings.InboundApiAllowedIPList, remoteIP)) @@ -54,8 +42,7 @@ public partial class InboundController : ControllerBase, IInboundController Content(_InfinityQSRepositoryV4.GetProductionSpecification(part)); + [HttpGet("{process}/last-group-id-with-value")] + public IActionResult GetLastGroupIdWithValue(string process, string? part, int? test) => + Content(_InfinityQSRepositoryV4.GetLastGroupIdWithValue(process, part, test)); + } \ No newline at end of file diff --git a/Server/OI.Metrology.Server.csproj b/Server/OI.Metrology.Server.csproj index f571fc1..1d496b7 100644 --- a/Server/OI.Metrology.Server.csproj +++ b/Server/OI.Metrology.Server.csproj @@ -25,10 +25,10 @@ - + - + @@ -38,9 +38,9 @@ - + - + diff --git a/Server/Program.cs b/Server/Program.cs index 613fa81..a2600e7 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -74,7 +74,6 @@ public class Program _ = webApplicationBuilder.Services.AddSingleton(); _ = webApplicationBuilder.Services.AddSingleton(); _ = webApplicationBuilder.Services.AddSingleton(); - _ = webApplicationBuilder.Services.AddSingleton(); _ = webApplicationBuilder.Services.AddSingleton(_ => clientSettingsRepository); _ = webApplicationBuilder.Services.AddSingleton(); _ = webApplicationBuilder.Services.AddSingleton(); diff --git a/Server/Repositories/FileShareRepository.cs b/Server/Repositories/FileShareRepository.cs index 72ca08b..d9c7650 100644 --- a/Server/Repositories/FileShareRepository.cs +++ b/Server/Repositories/FileShareRepository.cs @@ -64,15 +64,6 @@ public class FileShareRepository : IFileShareRepository throw new Exception(httpResponseMessage.Result.StatusCode.ToString()); } - void IFileShareRepository.FileWrite(HttpClient httpClient, string path, string contents) - { - Uri uri = GetEndPoint(httpClient, "file-write"); - Task httpResponseMessage = httpClient.GetAsync(uri); - httpResponseMessage.Wait(); - if (httpResponseMessage.Result.StatusCode != System.Net.HttpStatusCode.OK) - throw new Exception(httpResponseMessage.Result.StatusCode.ToString()); - } - HttpResponseMessage IFileShareRepository.ReadFile(HttpClient httpClient, Uri uri) { HttpResponseMessage result; diff --git a/Server/Repositories/InboundRepository.cs b/Server/Repositories/InboundRepository.cs index 4c986c5..187cff0 100644 --- a/Server/Repositories/InboundRepository.cs +++ b/Server/Repositories/InboundRepository.cs @@ -31,76 +31,42 @@ public class InboundRepository : IInboundRepository return false; } - // this is the main endpoint, it accepts a JSON message that contains both the header and data records together - // tooltype is the ToolTypeName column from the ToolType table - // JToken is how you can accept a JSON message without deserialization. - // Using "string" doesn't work because ASP.NET Core will expect a json encoded string, not give you the actual string. - DataResponse IInboundRepository.Data(IMetrologyRepository metrologyRepository, IInboundDataService inboundDataService, string tooltype, string? json) + DataResponse IInboundRepository.Data(IMetrologyRepository metrologyRepository, IInboundDataService inboundDataService, string toolTypeName, JsonElement? jsonElement) { DataResponse result = new(); - ToolType? toolType = metrologyRepository.GetToolTypeByName(tooltype); - if (toolType is null) - result.Errors.Add($"Invalid tool type: {tooltype}"); - else + if (jsonElement is null || jsonElement.Value.ValueKind != JsonValueKind.Object) + throw new Exception("Invalid body!"); + string? json = jsonElement.ToString(); + JToken jToken = (string.IsNullOrEmpty(json) ? JToken.Parse("{}") : JToken.Parse(json)) ?? + throw new Exception($"Invalid body: {json}"); + ToolType toolType = metrologyRepository.GetToolTypeByName(toolTypeName) ?? + throw new Exception($"Invalid tool type: {toolTypeName}"); + List metaData = metrologyRepository.GetToolTypeMetadataByToolTypeID(toolType.ID).ToList(); + inboundDataService.ValidateJSONFields(jToken, 0, metaData, result.Errors, result.Warnings); + if (result.Errors.Count == 0) { - InboundCommon? inboundCommon = string.IsNullOrEmpty(json) ? null : JsonSerializer.Deserialize(json); - if (inboundCommon is null || string.IsNullOrEmpty(inboundCommon.ProcessDataStandardFormat)) - result.Errors.Add($"Invalid body: {json}"); - else + try { - string? sourceDirectory = Path.GetDirectoryName(inboundCommon.ProcessDataStandardFormat); - string? parentDirectory = Path.GetDirectoryName(sourceDirectory); - if (string.IsNullOrEmpty(sourceDirectory) || string.IsNullOrEmpty(parentDirectory) || !Directory.Exists(parentDirectory)) - result.Errors.Add($"Invalid body:path: <{inboundCommon.ProcessDataStandardFormat}>"); - else - { - JToken jToken = string.IsNullOrEmpty(json) ? JToken.Parse("{}") : JToken.Parse(json); - if (jToken is null) - result.Errors.Add($"Invalid body: {json}"); - else - { - List metaData = metrologyRepository.GetToolTypeMetadataByToolTypeID(toolType.ID).ToList(); - if (metaData is null) - result.Errors.Add($"Invalid metadata for tool type: {tooltype}"); - else - { - inboundDataService.ValidateJSONFields(jToken, 0, metaData, result.Errors, result.Warnings); - if (result.Errors.Count == 0) - { - try - { - result.HeaderID = inboundDataService.DoSQLInsert(jToken, toolType, metaData); - result.Success = result.HeaderID > 0; - string? destinationDirectory = Path.Combine(parentDirectory, result.HeaderID.ToString()); - Directory.Move(sourceDirectory, destinationDirectory); - } - catch (Exception ex) { result.Errors.Add(ex.Message); } - } - } - } - } + result.HeaderID = inboundDataService.DoSQLInsert(jToken, toolType, metaData); + result.Success = result.HeaderID > 0; } + catch (Exception ex) { result.Errors.Add(ex.Message); } } + return result; } - // this is the endpoint for attaching a field. It is not JSON, it is form-data/multipart like an HTML form because that's the normal way. - // header ID is the ID value from the Header table - // datauniqueid is the Title value from the Data/Detail table - string? IInboundRepository.AttachFile(IMetrologyRepository metrologyRepository, IAttachmentsService attachmentsService, string tooltype, long headerid, string datauniqueid, string fileName, object uploadedFile) + void IInboundRepository.AttachFile(IMetrologyRepository metrologyRepository, IAttachmentsService attachmentsService, string toolTypeName, Attachment? attachment) { - string? result = null; - ToolType toolType = metrologyRepository.GetToolTypeByName(tooltype); - if (toolType is null) - result = $"Invalid tool type: {tooltype}"; - string filename = Path.GetFileName(fileName); - if (string.IsNullOrWhiteSpace(filename)) - result = "Empty filename"; - if (filename.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) - result = "Invalid filename"; - if (result is null && toolType is not null) - attachmentsService.SaveAttachment(toolType, headerid, datauniqueid, filename, uploadedFile); - return result; + if (attachment is null) + throw new Exception("Invalid body!"); + ToolType toolType = metrologyRepository.GetToolTypeByName(toolTypeName) ?? + throw new Exception($"Invalid tool type: {toolTypeName}"); + if (string.IsNullOrWhiteSpace(attachment.DestinationFileName)) + throw new Exception("Empty filename"); + if (attachment.DestinationFileName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + throw new Exception("Invalid filename"); + attachmentsService.SaveAttachment(toolType, attachment); } } \ No newline at end of file diff --git a/Server/Repositories/InfinityQSV4Repository.cs b/Server/Repositories/InfinityQSV4Repository.cs index 3a081d4..5fda686 100644 --- a/Server/Repositories/InfinityQSV4Repository.cs +++ b/Server/Repositories/InfinityQSV4Repository.cs @@ -37,119 +37,117 @@ public class InfinityQSV4Repository : IInfinityQSV4Repository string IInfinityQSV4Repository.GetCommandText(string subGroupId) { // cSpell:disable - StringBuilder result = new(); + List results = []; if (string.IsNullOrEmpty(subGroupId)) throw new ArgumentException(null, nameof(subGroupId)); - _ = result - .AppendLine(" select ") - .AppendLine(" sd.f_sgrp sd_sgrp, ") - .AppendLine(" sd.f_tsno sd_tsno, ") - .AppendLine(" dd.f_dsgp dd_dsgp, ") - .AppendLine(" dg.f_name gd_name, ") - .AppendLine(" dd.f_name dd_name ") - .AppendLine(" from [SPCEPIWORLD].[dbo].[SGRP_DSC] sd ") - .AppendLine(" join [SPCEPIWORLD].[dbo].[DESC_DAT] dd ") - .AppendLine(" on sd.f_dsgp = dd.f_dsgp ") - .AppendLine(" and sd.f_desc = dd.f_desc ") - .AppendLine(" join [SPCEPIWORLD].[dbo].[DESC_GRP] dg ") - .AppendLine(" on dd.f_dsgp = dg.f_dsgp "); - _ = result.Append(" where sd.f_sgrp = ").Append(subGroupId).AppendLine(" "); - _ = result.AppendLine(" for json path "); - return result.ToString(); + results.Add(" select "); + results.Add(" sd.f_sgrp sd_sgrp, "); + results.Add(" sd.f_tsno sd_tsno, "); + results.Add(" dd.f_dsgp dd_dsgp, "); + results.Add(" dg.f_name gd_name, "); + results.Add(" dd.f_name dd_name "); + results.Add(" from [SPCEPIWORLD].[dbo].[SGRP_DSC] sd "); + results.Add(" join [SPCEPIWORLD].[dbo].[DESC_DAT] dd "); + results.Add(" on sd.f_dsgp = dd.f_dsgp "); + results.Add(" and sd.f_desc = dd.f_desc "); + results.Add(" join [SPCEPIWORLD].[dbo].[DESC_GRP] dg "); + results.Add(" on dd.f_dsgp = dg.f_dsgp "); + results.Add($" where sd.f_sgrp = {subGroupId} "); + results.Add(" for json path "); + return string.Join(' ', results); } // cSpell:enable string IInfinityQSV4Repository.GetCommandText(string? subGroupId, string? process, string? job, string? part, string? lot, string? dateTime) { // cSpell:disable - StringBuilder result = new(); + List results = []; const string dateTimeFormat = "yyyy-MM-dd HH:mm:ss"; if (!string.IsNullOrEmpty(dateTime) && (dateTime.Contains('-') || dateTime.Contains(' ') || dateTime.Contains(':')) && dateTime.Length != dateTimeFormat.Length) throw new ArgumentException(null, nameof(dateTime)); - _ = result - .AppendLine(" select case when iq.sl_loos is null then 0 else iq.sl_loos end + ") - .AppendLine(" case when iq.sl_uoos is null then 0 else iq.sl_uoos end + ") - .AppendLine(" iq.ev_count as iq_sum, ") - .AppendLine(" iq.sl_aflag, ") - .AppendLine(" iq.sl_loos, ") - .AppendLine(" iq.sl_uoos, ") - .AppendLine(" iq.se_sgrp, ") - .AppendLine(" iq.se_sgtm, ") - .AppendLine(" iq.se_tsno, ") - .AppendLine(" iq.td_test, ") - .AppendLine(" iq.pr_name, ") - .AppendLine(" iq.jd_name, ") - .AppendLine(" iq.pl_name, ") - .AppendLine(" iq.pd_name, ") - .AppendLine(" iq.td_name, ") - .AppendLine(" iq.se_val, ") - .AppendLine(" iq.sl_eflag, ") - .AppendLine(" iq.sl_scal, ") - .AppendLine(" iq.sl_sls, ") - .AppendLine(" iq.sl_usl ") - .AppendLine(" from ( ") - .AppendLine(" select ") - .AppendLine(" se.f_sgrp se_sgrp, ") - .AppendLine(" se.f_sgtm se_sgtm, ") - .AppendLine(" se.f_tsno se_tsno, ") - .AppendLine(" se.f_val se_val, ") - .AppendLine(" pr.f_name pr_name, ") - .AppendLine(" jd.f_name jd_name, ") - .AppendLine(" pl.f_name pl_name, ") - .AppendLine(" pd.f_name pd_name, ") - .AppendLine(" td.f_test td_test, ") - .AppendLine(" td.f_name td_name, ") - .AppendLine(" sl.f_eflag sl_eflag, ") - .AppendLine(" sl.f_aflag sl_aflag, ") - .AppendLine(" sl.f_scal sl_scal, ") - .AppendLine(" sl.f_lsl sl_sls, ") - .AppendLine(" sl.f_usl sl_usl, ") - .AppendLine(" case when sl.f_aflag is null or sl.f_aflag = 0 then null else ") - .AppendLine(" case when round(se.f_val, sl.F_scal, 1) < sl.f_lsl then 1 else 0 end ") - .AppendLine(" end as sl_loos, ") - .AppendLine(" case when sl.f_aflag is null or sl.f_aflag = 0 then null else ") - .AppendLine(" case when round(se.f_val, sl.F_scal, 1) > sl.f_usl then 1 else 0 end ") - .AppendLine(" end as sl_uoos, ") - .AppendLine(" (select count(ev.f_evnt) ") - .AppendLine(" from [spcepiworld].[dbo].[evnt_inf] ev ") - .AppendLine(" where ev.f_prcs = pr.f_prcs ") - .AppendLine(" and ev.f_part = pd.f_part ") - .AppendLine(" and ev.f_sgtm = se.f_sgtm ") - .AppendLine(" ) ev_count ") - .AppendLine(" from [spcepiworld].[dbo].[sgrp_ext] se ") - .AppendLine(" join [spcepiworld].[dbo].[prcs_dat] pr ") - .AppendLine(" on se.f_prcs = pr.f_prcs ") - .AppendLine(" join [spcepiworld].[dbo].[job_dat] jd ") - .AppendLine(" on se.f_job = jd.f_job ") - .AppendLine(" join [spcepiworld].[dbo].[part_lot] pl ") - .AppendLine(" on se.f_lot = pl.f_lot ") - .AppendLine(" join [spcepiworld].[dbo].[part_dat] pd ") - .AppendLine(" on se.f_part = pd.f_part ") - .AppendLine(" join [spcepiworld].[dbo].[test_dat] td ") - .AppendLine(" on se.f_test = td.f_test ") - .AppendLine(" left join [spcepiworld].[dbo].[spec_lim] sl ") - .AppendLine(" on se.f_part = sl.f_part ") - .AppendLine(" and se.f_test = sl.f_test ") - .AppendLine(" where se.f_flag = 0 ") - .AppendLine(" and (sl.f_prcs is null or se.f_prcs = sl.f_prcs or sl.f_prcs = 0) "); + results.Add(" select case when iq.sl_loos is null then 0 else iq.sl_loos end + "); + results.Add(" case when iq.sl_uoos is null then 0 else iq.sl_uoos end + "); + results.Add(" iq.ev_count as iq_sum, "); + results.Add(" iq.sl_aflag, "); + results.Add(" iq.sl_loos, "); + results.Add(" iq.sl_uoos, "); + results.Add(" iq.se_sgrp, "); + results.Add(" iq.se_sgtm, "); + results.Add(" iq.se_tsno, "); + results.Add(" iq.td_test, "); + results.Add(" iq.pr_name, "); + results.Add(" iq.jd_name, "); + results.Add(" iq.pl_name, "); + results.Add(" iq.pd_name, "); + results.Add(" iq.td_name, "); + results.Add(" iq.se_val, "); + results.Add(" iq.sl_eflag, "); + results.Add(" iq.sl_scal, "); + results.Add(" iq.sl_sls, "); + results.Add(" iq.sl_usl "); + results.Add(" from ( "); + results.Add(" select "); + results.Add(" se.f_sgrp se_sgrp, "); + results.Add(" se.f_sgtm se_sgtm, "); + results.Add(" se.f_tsno se_tsno, "); + results.Add(" se.f_val se_val, "); + results.Add(" pr.f_name pr_name, "); + results.Add(" jd.f_name jd_name, "); + results.Add(" pl.f_name pl_name, "); + results.Add(" pd.f_name pd_name, "); + results.Add(" td.f_test td_test, "); + results.Add(" td.f_name td_name, "); + results.Add(" sl.f_eflag sl_eflag, "); + results.Add(" sl.f_aflag sl_aflag, "); + results.Add(" sl.f_scal sl_scal, "); + results.Add(" sl.f_lsl sl_sls, "); + results.Add(" sl.f_usl sl_usl, "); + results.Add(" case when sl.f_aflag is null or sl.f_aflag = 0 then null else "); + results.Add(" case when round(se.f_val, sl.F_scal, 1) < sl.f_lsl then 1 else 0 end "); + results.Add(" end as sl_loos, "); + results.Add(" case when sl.f_aflag is null or sl.f_aflag = 0 then null else "); + results.Add(" case when round(se.f_val, sl.F_scal, 1) > sl.f_usl then 1 else 0 end "); + results.Add(" end as sl_uoos, "); + results.Add(" (select count(ev.f_evnt) "); + results.Add(" from [spcepiworld].[dbo].[evnt_inf] ev "); + results.Add(" where ev.f_prcs = pr.f_prcs "); + results.Add(" and ev.f_part = pd.f_part "); + results.Add(" and ev.f_sgtm = se.f_sgtm "); + results.Add(" ) ev_count "); + results.Add(" from [spcepiworld].[dbo].[sgrp_ext] se "); + results.Add(" join [spcepiworld].[dbo].[prcs_dat] pr "); + results.Add(" on se.f_prcs = pr.f_prcs "); + results.Add(" join [spcepiworld].[dbo].[job_dat] jd "); + results.Add(" on se.f_job = jd.f_job "); + results.Add(" join [spcepiworld].[dbo].[part_lot] pl "); + results.Add(" on se.f_lot = pl.f_lot "); + results.Add(" join [spcepiworld].[dbo].[part_dat] pd "); + results.Add(" on se.f_part = pd.f_part "); + results.Add(" join [spcepiworld].[dbo].[test_dat] td "); + results.Add(" on se.f_test = td.f_test "); + results.Add(" left join [spcepiworld].[dbo].[spec_lim] sl "); + results.Add(" on se.f_part = sl.f_part "); + results.Add(" and se.f_test = sl.f_test "); + results.Add(" where se.f_flag = 0 "); + results.Add(" and (sl.f_prcs is null or se.f_prcs = sl.f_prcs or sl.f_prcs = 0) "); if (!string.IsNullOrEmpty(subGroupId)) - _ = result.Append(" and se.f_sgrp = ").Append(subGroupId.Split(" ")[0]).AppendLine(" "); + results.Add($" and se.f_sgrp = {subGroupId.Split(" ")[0]} "); if (!string.IsNullOrEmpty(process)) - _ = result.Append(" and pr.f_name = '").Append(process).AppendLine("' "); + results.Add($" and pr.f_name = '{process}' "); if (!string.IsNullOrEmpty(part)) - _ = result.Append(" and pd.f_name = '").Append(part).AppendLine("' "); + results.Add($" and pd.f_name = '{part}' "); if (!string.IsNullOrEmpty(job)) - _ = result.Append(" and jd.f_name = '").Append(job).AppendLine("' "); + results.Add($" and jd.f_name = '{job}' "); if (!string.IsNullOrEmpty(lot)) - _ = result.Append(" and pl.f_name = '").Append(lot).AppendLine("' "); + results.Add($" and pl.f_name = '{lot}' "); if (!string.IsNullOrEmpty(dateTime) && (dateTime.Contains('-') || dateTime.Contains(' ') || dateTime.Contains(':'))) - _ = result.Append(" and dateadd(HH, -7, (dateadd(SS, convert(bigint, se.f_sgtm), '19700101'))) = '").Append(dateTime).AppendLine("' "); - _ = result.AppendLine(" ) as iq ") - .AppendLine(" order by iq.sl_loos + iq.sl_uoos + iq.ev_count desc, ") - .AppendLine(" iq.sl_aflag desc, ") - .AppendLine(" iq.se_sgrp, ") - .AppendLine(" iq.se_tsno, ") - .AppendLine(" iq.td_test ") - .AppendLine(" for json path "); - return result.ToString(); + results.Add($" and dateadd(HH, -7, (dateadd(SS, convert(bigint, se.f_sgtm), '19700101'))) = '{dateTime}' "); + results.Add(" ) as iq "); + results.Add(" order by iq.sl_loos + iq.sl_uoos + iq.ev_count desc, "); + results.Add(" iq.sl_aflag desc, "); + results.Add(" iq.se_sgrp, "); + results.Add(" iq.se_tsno, "); + results.Add(" iq.td_test "); + results.Add(" for json path "); + return string.Join(' ', results); } // cSpell:enable private JsonElement[] GetAllReactorsAsJsonElementElement() @@ -293,33 +291,32 @@ public class InfinityQSV4Repository : IInfinityQSV4Repository string IInfinityQSV4Repository.GetCommandText(InfinityQSV4 infinityQSV4) { // cSpell:disable - StringBuilder result = new(); + List results = []; if (string.IsNullOrEmpty(infinityQSV4.Process)) throw new ArgumentException(nameof(infinityQSV4.Process)); if (string.IsNullOrEmpty(infinityQSV4.Part)) throw new ArgumentException(nameof(infinityQSV4.Part)); - _ = result - .AppendLine(" select ") - .AppendLine(" ev.f_evnt [ev_evnt], ") - .AppendLine(" ev.f_sgtm [ev_sgtm], ") - .AppendLine(" dateadd(HH, -7, (dateadd(SS, convert(bigint, ev.f_sgtm), '19700101'))) [ev_utc7], ") - .AppendLine(" pr.f_name [pr_name], ") - .AppendLine(" pd.f_name [pd_name], ") - .AppendLine(" td.f_test [td_test], ") - .AppendLine(" td.f_name [td_name], ") - .AppendLine(" ev.f_name [ev_name] ") - .AppendLine(" from [spcepiworld].[dbo].[evnt_inf] ev ") - .AppendLine(" join [spcepiworld].[dbo].[prcs_dat] pr ") - .AppendLine(" on ev.f_prcs = pr.f_prcs ") - .AppendLine(" join [spcepiworld].[dbo].[part_dat] pd ") - .AppendLine(" on ev.f_part = pd.f_part ") - .AppendLine(" join [spcepiworld].[dbo].[test_dat] td ") - .AppendLine(" on ev.f_test = td.f_test ") - .Append(" where pr.f_name = '").Append(infinityQSV4.Process).AppendLine("' ") - .Append(" and pd.f_name = '").Append(infinityQSV4.Part).AppendLine("' ") - .Append(" and ev.f_sgtm = ").Append(infinityQSV4.SubGroupDateTime).AppendLine(" ") - .AppendLine(" for json path "); - return result.ToString(); + results.Add(" select "); + results.Add(" ev.f_evnt [ev_evnt], "); + results.Add(" ev.f_sgtm [ev_sgtm], "); + results.Add(" dateadd(HH, -7, (dateadd(SS, convert(bigint, ev.f_sgtm), '19700101'))) [ev_utc7], "); + results.Add(" pr.f_name [pr_name], "); + results.Add(" pd.f_name [pd_name], "); + results.Add(" td.f_test [td_test], "); + results.Add(" td.f_name [td_name], "); + results.Add(" ev.f_name [ev_name] "); + results.Add(" from [spcepiworld].[dbo].[evnt_inf] ev "); + results.Add(" join [spcepiworld].[dbo].[prcs_dat] pr "); + results.Add(" on ev.f_prcs = pr.f_prcs "); + results.Add(" join [spcepiworld].[dbo].[part_dat] pd "); + results.Add(" on ev.f_part = pd.f_part "); + results.Add(" join [spcepiworld].[dbo].[test_dat] td "); + results.Add(" on ev.f_test = td.f_test "); + results.Add($" where pr.f_name = '{infinityQSV4.Process}' "); + results.Add($" and pd.f_name = '{infinityQSV4.Part}' "); + results.Add($" and ev.f_sgtm = {infinityQSV4.SubGroupDateTime} "); + results.Add(" for json path "); + return string.Join(' ', results); } // cSpell:enable Result IInfinityQSV4Repository.GetHeader(string subGroupId) @@ -348,28 +345,27 @@ public class InfinityQSV4Repository : IInfinityQSV4Repository string IInfinityQSV4Repository.GetCommandText(string process, string? part) { // cSpell:disable - StringBuilder result = new(); + List results = []; if (string.IsNullOrEmpty(process)) throw new ArgumentException(null, nameof(process)); if (string.IsNullOrEmpty(part)) throw new ArgumentException(null, nameof(part)); - _ = result - .AppendLine(" select [f_mean] as ProcessMean, ") - .AppendLine(" [f_sp] as ProcessSigma ") - .AppendLine(" from [spcepiworld].[dbo].[test_dat] test ") - .AppendLine(" join [spcepiworld].[dbo].[ctrl_lim] ctrl ") - .AppendLine(" on test.f_test = ctrl.f_test ") - .AppendLine(" and test.f_tsgp = 1104848523 /* Product Data */ ") - .AppendLine(" join [spcepiworld].[dbo].[part_dat] part ") - .AppendLine(" on part.f_part = ctrl.f_part ") - .AppendLine(" and ctrl.f_test = 1125073605 /* Average Sum of Defects */ ") - .AppendLine(" join [spcepiworld].[dbo].[prcs_dat] process ") - .AppendLine(" on process.f_prcs = ctrl.f_prcs ") - .AppendLine(" where test.f_name = 'Average Sum of Defects' ") - .Append(" and process.f_name = '").Append(process).AppendLine("' ") - .Append(" and part.f_name = '").Append(part).AppendLine("' ") - .AppendLine(" for json path; "); - return result.ToString(); + results.Add(" select [f_mean] as ProcessMean, "); + results.Add(" [f_sp] as ProcessSigma "); + results.Add(" from [spcepiworld].[dbo].[test_dat] test "); + results.Add(" join [spcepiworld].[dbo].[ctrl_lim] ctrl "); + results.Add(" on test.f_test = ctrl.f_test "); + results.Add(" and test.f_tsgp = 1104848523 /* Product Data */ "); + results.Add(" join [spcepiworld].[dbo].[part_dat] part "); + results.Add(" on part.f_part = ctrl.f_part "); + results.Add(" and ctrl.f_test = 1125073605 /* Average Sum of Defects */ "); + results.Add(" join [spcepiworld].[dbo].[prcs_dat] process "); + results.Add(" on process.f_prcs = ctrl.f_prcs "); + results.Add(" where test.f_name = 'Average Sum of Defects' "); + results.Add($" and process.f_name = '{process}' "); + results.Add($" and part.f_name = '{part}' "); + results.Add(" for json path; "); + return string.Join(' ', results); } // cSpell:enable string IInfinityQSV4Repository.GetProductDataAverageSumOfDefectsProcessMeanProcessSigma(string process, string? recipe) @@ -393,53 +389,52 @@ public class InfinityQSV4Repository : IInfinityQSV4Repository string IInfinityQSV4Repository.GetCommandText(List eppReactorNumbers) { // cSpell:disable - StringBuilder result = new(); - _ = result - .AppendLine(" select se.f_sgrp, ") - .AppendLine(" dateadd(HH, -7, (dateadd(SS, convert(bigint, se.f_sgrp), '19700101'))) date_time, ") - .AppendLine(" iq.pr_name, ") - .AppendLine(" iq.pd_name, ") - .AppendLine(" max(case ") - .AppendLine(" when td.f_test = 1104769646 ") - .AppendLine(" then se.f_val ") - .AppendLine(" else null ") - .AppendLine(" end) as iq_value, ") - .AppendLine(" max(case ") - .AppendLine(" when td.f_test = 1312288843 ") - .AppendLine(" then se.f_val else null ") - .AppendLine(" end) as iq_temp_offset_percent ") - .AppendLine(" from ( ") - .AppendLine(" select ") - .AppendLine(" max(se.f_sgrp) se_max_sgrp, ") - .AppendLine(" se.f_test se_test, ") - .AppendLine(" pr.f_name pr_name, ") - .AppendLine(" pd.f_name pd_name ") - .AppendLine(" from [spcepiworld].[dbo].[sgrp_ext] se ") - .AppendLine(" join [spcepiworld].[dbo].[prcs_dat] pr ") - .AppendLine(" on se.f_prcs = pr.f_prcs ") - .AppendLine(" join [spcepiworld].[dbo].[part_dat] pd ") - .AppendLine(" on se.f_part = pd.f_part ") - .AppendLine(" where se.f_flag = 0 ") - .Append(" and pr.f_name in (").Append(string.Join(',', eppReactorNumbers)).AppendLine(") ") - .AppendLine(" and pd.f_name = '1090 - Full Load' ") - .AppendLine(" and se.f_test in (1104769646, 1312288843) ") - .AppendLine(" group by se.f_test, ") - .AppendLine(" pr.f_name, ") - .AppendLine(" pd.f_name ") - .AppendLine(" ) as iq ") - .AppendLine(" join [spcepiworld].[dbo].[sgrp_ext] se ") - .AppendLine(" on iq.se_max_sgrp = se.f_sgrp ") - .AppendLine(" join [spcepiworld].[dbo].[test_dat] td ") - .AppendLine(" on iq.se_test = td.f_test ") - .AppendLine(" and se.f_test = td.f_test ") - .AppendLine(" where se.f_flag = 0 ") - .AppendLine(" and td.f_test in (1104769646, 1312288843) ") - .AppendLine(" group by se.f_sgrp, ") - .AppendLine(" iq.pr_name, ") - .AppendLine(" iq.pd_name ") - .AppendLine(" order by iq.pr_name ") - .AppendLine(" for json path; "); - return result.ToString(); + List results = []; + results.Add(" select se.f_sgrp, "); + results.Add(" dateadd(HH, -7, (dateadd(SS, convert(bigint, se.f_sgrp), '19700101'))) date_time, "); + results.Add(" iq.pr_name, "); + results.Add(" iq.pd_name, "); + results.Add(" max(case "); + results.Add(" when td.f_test = 1104769646 "); + results.Add(" then se.f_val "); + results.Add(" else null "); + results.Add(" end) as iq_value, "); + results.Add(" max(case "); + results.Add(" when td.f_test = 1312288843 "); + results.Add(" then se.f_val else null "); + results.Add(" end) as iq_temp_offset_percent "); + results.Add(" from ( "); + results.Add(" select "); + results.Add(" max(se.f_sgrp) se_max_sgrp, "); + results.Add(" se.f_test se_test, "); + results.Add(" pr.f_name pr_name, "); + results.Add(" pd.f_name pd_name "); + results.Add(" from [spcepiworld].[dbo].[sgrp_ext] se "); + results.Add(" join [spcepiworld].[dbo].[prcs_dat] pr "); + results.Add(" on se.f_prcs = pr.f_prcs "); + results.Add(" join [spcepiworld].[dbo].[part_dat] pd "); + results.Add(" on se.f_part = pd.f_part "); + results.Add(" where se.f_flag = 0 "); + results.Add($" and pr.f_name in ({string.Join(',', eppReactorNumbers)} "); + results.Add(" and pd.f_name = '1090 - Full Load' "); + results.Add(" and se.f_test in (1104769646, 1312288843) "); + results.Add(" group by se.f_test, "); + results.Add(" pr.f_name, "); + results.Add(" pd.f_name "); + results.Add(" ) as iq "); + results.Add(" join [spcepiworld].[dbo].[sgrp_ext] se "); + results.Add(" on iq.se_max_sgrp = se.f_sgrp "); + results.Add(" join [spcepiworld].[dbo].[test_dat] td "); + results.Add(" on iq.se_test = td.f_test "); + results.Add(" and se.f_test = td.f_test "); + results.Add(" where se.f_flag = 0 "); + results.Add(" and td.f_test in (1104769646, 1312288843) "); + results.Add(" group by se.f_sgrp, "); + results.Add(" iq.pr_name, "); + results.Add(" iq.pd_name "); + results.Add(" order by iq.pr_name "); + results.Add(" for json path; "); + return string.Join(' ', results); } // cSpell:enable List IInfinityQSV4Repository.GetEpiProTempVerificationRows(int[] night) @@ -651,4 +646,55 @@ public class InfinityQSV4Repository : IInfinityQSV4Repository return result; } + private static string GetCommandText(string process, string? part, int? test) + { // cSpell:disable + List results = []; + if (string.IsNullOrEmpty(process)) + throw new ArgumentException(null, nameof(process)); + if (string.IsNullOrEmpty(part)) + throw new ArgumentException(null, nameof(part)); + if (test is null) + throw new ArgumentException(null, nameof(test)); + results.Add(" select se_max_sgrp, se.f_val se_value "); + results.Add(" from ( "); + results.Add(" select "); + results.Add(" max(se.f_sgrp) se_max_sgrp "); + results.Add(" from [spcepiworld].[dbo].[sgrp_ext] se "); + results.Add(" join [spcepiworld].[dbo].[prcs_dat] pr "); + results.Add(" on se.f_prcs = pr.f_prcs "); + results.Add(" join [spcepiworld].[dbo].[part_dat] pd "); + results.Add(" on se.f_part = pd.f_part "); + results.Add(" where se.f_flag = 0 "); + results.Add(" and se.f_tsno = 1 "); + results.Add($" and pr.f_name = '{process}' "); + results.Add($" and pd.f_name = '{part}' "); + results.Add($" and se.f_test = {test.Value} "); + results.Add(" ) as iq "); + results.Add(" join [spcepiworld].[dbo].[sgrp_ext] se "); + results.Add(" on se_max_sgrp = se.f_sgrp "); + results.Add(" where se.f_flag = 0 "); + results.Add(" and se.f_tsno = 1 "); + results.Add($" and se.f_test = {test.Value} "); + results.Add(" for json path "); + return string.Join(' ', results); + } // cSpell:enable + + string IInfinityQSV4Repository.GetLastGroupIdWithValue(string process, string? part, int? test) + { + StringBuilder result; + if (!string.IsNullOrEmpty(_MockRoot)) + { + string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), $"{_RepositoryName}-{nameof(IInfinityQSV4Repository.GetLastGroupIdWithValue)}.json")); + result = new(json); + } + else + { + string commandText = GetCommandText(process, part, test); + result = GetForJsonPath(_DBConnectionFactory, commandText, useIqsConnection: false); + if (result.Length == 0) + result = new("{}"); + } + return result.ToString(); + } + } \ No newline at end of file diff --git a/Server/Repositories/MetrologyRepository.cs b/Server/Repositories/MetrologyRepository.cs index 7f50f7c..4600f47 100644 --- a/Server/Repositories/MetrologyRepository.cs +++ b/Server/Repositories/MetrologyRepository.cs @@ -16,6 +16,7 @@ namespace OI.Metrology.Server.Repositories; public class MetrologyRepository : IMetrologyRepository { + private readonly string _MockRoot; private readonly string _RepositoryName; private readonly IMemoryCache _MemoryCache; @@ -32,7 +33,8 @@ public class MetrologyRepository : IMetrologyRepository protected DbProviderFactory GetDbProviderFactory(IDbConnection conn) => DbProviderFactories.GetFactory(conn.GetType().Namespace); - internal static TransactionScope StartTransaction() => new(); + internal static TransactionScope StartTransaction() => + new(); protected void CacheItem(string key, object v) { @@ -56,7 +58,7 @@ public class MetrologyRepository : IMetrologyRepository return r; } - internal ToolType GetToolTypeByName(string name) + ToolType IMetrologyRepository.GetToolTypeByName(string name) { ToolType cached; string cacheKey = "GetToolTypeByName_" + name; @@ -107,7 +109,7 @@ public class MetrologyRepository : IMetrologyRepository return r; } - internal long InsertToolDataJSON(JToken jsonrow, long headerId, List metaData, string tableName) + long IMetrologyRepository.InsertToolDataJSON(JToken jsonrow, long headerId, List metaData, string tableName) { long r = -1; using (DbConnection conn = _DBConnectionFactory.GetDbConnection()) @@ -115,7 +117,8 @@ public class MetrologyRepository : IMetrologyRepository bool isHeader = headerId <= 0; // get fields from metadata - List fields = metaData.Where(md => md.Header == isHeader).ToList(); + List fields = metaData.Where(md => + md.Header == isHeader).ToList(); // maps ApiName to ColumnName Dictionary fieldmap = new(); @@ -155,7 +158,8 @@ public class MetrologyRepository : IMetrologyRepository else { // Find the container field in the json - JProperty contJP = jsonrow.Children().Where(c => string.Equals(c.Name.Trim(), containerField, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); + JProperty contJP = jsonrow.Children().Where(c => + string.Equals(c.Name.Trim(), containerField, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); if ((contJP is not null) && (contJP.Value is JArray array)) { @@ -182,7 +186,7 @@ public class MetrologyRepository : IMetrologyRepository _ = cmd.Parameters.Add(p); } - protected long InsertRowFromJSON( + private long InsertRowFromJSON( IDbConnection conn, string tableName, JToken jsonrow, @@ -262,7 +266,7 @@ public class MetrologyRepository : IMetrologyRepository return Convert.ToInt64(o); } - internal DataTable ExportData(string spName, DateTime startTime, DateTime endTime) + DataTable IMetrologyRepository.ExportData(string spName, DateTime startTime, DateTime endTime) { DataTable dt = new(); DateTime endTimeLocal = endTime.ToLocalTime(); @@ -312,7 +316,7 @@ public class MetrologyRepository : IMetrologyRepository return sb.ToString(); } - internal DataTable GetHeaders(int toolTypeId, string? startTime, string? endTime, int? pageNo, int? pageSize, long? headerId, out long totalRecords) + DataTable IMetrologyRepository.GetHeaders(int toolTypeId, string? startTime, string? endTime, int? pageNo, int? pageSize, long? headerId, out long totalRecords) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); @@ -324,7 +328,8 @@ public class MetrologyRepository : IMetrologyRepository StringBuilder sb = new(); _ = sb.Append( FormDynamicSelectQuery( - md.Where(m => m.Header == true).ToList(), + md.Where(m => + m.Header == true).ToList(), tt.HeaderTableName) ); @@ -401,7 +406,7 @@ public class MetrologyRepository : IMetrologyRepository return dt; } - internal DataTable GetData(int toolTypeId, long headerid) + DataTable IMetrologyRepository.GetData(int toolTypeId, long headerid) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); @@ -413,7 +418,9 @@ public class MetrologyRepository : IMetrologyRepository StringBuilder sb = new(); _ = sb.Append( FormDynamicSelectQuery( - md.Where(m => m.Header == false).OrderBy(m => m.GridDisplayOrder).ToList(), + md.Where(m => + m.Header == false).OrderBy(m => + m.GridDisplayOrder).ToList(), tt.DataTableName) ); @@ -492,17 +499,32 @@ public class MetrologyRepository : IMetrologyRepository return dt; } - internal Guid GetHeaderAttachmentID(int toolTypeId, long headerId) + Guid IMetrologyRepository.GetHeaderAttachmentID(int toolTypeId, long headerId) + { + Guid result; + ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); + + using DbConnection conn = _DBConnectionFactory.GetDbConnection(); + string sql = + $"SELECT AttachmentID FROM [{tt.HeaderTableName}] WHERE ID = @HeaderID"; + result = conn.ExecuteScalar(sql, param: new { HeaderID = headerId }); + return result; + } + + void IMetrologyRepository.SetHeaderAttachmentID(int toolTypeId, long headerId, string attachmentId) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = - $"UPDATE [{tt.HeaderTableName}] SET AttachmentID = NEWID() WHERE ID = @HeaderID AND AttachmentID IS NULL; " + + $"UPDATE [{tt.HeaderTableName}] SET AttachmentID = @AttachmentID WHERE ID = @HeaderID AND AttachmentID IS NULL; " + $"SELECT AttachmentID FROM [{tt.HeaderTableName}] WHERE ID = @HeaderID"; - return conn.ExecuteScalar(sql, param: new { HeaderID = headerId }); + Guid guid = conn.ExecuteScalar(sql, param: new { HeaderID = headerId, AttachmentID = attachmentId }); + if (attachmentId != guid.ToString()) + throw new NotSupportedException($"{attachmentId} != {guid}"); } - internal string GetHeaderInsertDate(int toolTypeId, long headerId) + + string IMetrologyRepository.GetHeaderInsertDate(int toolTypeId, long headerId) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); @@ -511,7 +533,8 @@ public class MetrologyRepository : IMetrologyRepository $"SELECT CONVERT(varchar, case when [InsertDate] < [Date] or [Date] is null then [InsertDate] else [Date] end, 120) d FROM[{tt.HeaderTableName}] where ID = @HeaderID"; return conn.ExecuteScalar(sql, param: new { HeaderID = headerId }); } - internal string GetAttachmentInsertDateByGUID(string tableName, Guid attachmentId) + + string IMetrologyRepository.GetAttachmentInsertDateByGUID(string tableName, Guid attachmentId) { using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = ""; @@ -526,24 +549,27 @@ public class MetrologyRepository : IMetrologyRepository return conn.ExecuteScalar(sql, param: new { AttachmentID = attachmentId }); } - internal void SetHeaderDirName(string tableName, long headerId, string dateDir) + + void IMetrologyRepository.SetHeaderDirName(string tableName, long headerId, string dateDir) { using DbConnection conn = _DBConnectionFactory.GetDbConnection(); _ = conn.Execute($"UPDATE [{tableName}] SET AttachDirName = @AttachDirName WHERE ID = @HeaderID;", new { HeaderID = headerId, AttachDirName = dateDir }); } - internal Guid GetDataAttachmentID(int toolTypeId, long headerId, string title) + void IMetrologyRepository.SetDataAttachmentID(int toolTypeId, long headerId, string title, string attachmentId) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = - $"UPDATE [{tt.DataTableName}] SET AttachmentID = NEWID() WHERE HeaderID = @HeaderID AND Title = @Title AND AttachmentID IS NULL; " + + $"UPDATE [{tt.DataTableName}] SET AttachmentID = @AttachmentID WHERE HeaderID = @HeaderID AND Title = @Title AND AttachmentID IS NULL; " + $"SELECT AttachmentID FROM [{tt.DataTableName}] WHERE HeaderID = @HeaderID AND Title = @Title"; - return conn.ExecuteScalar(sql, param: new { HeaderID = headerId, Title = title }); + Guid guid = conn.ExecuteScalar(sql, param: new { HeaderID = headerId, AttachmentID = attachmentId, Title = title }); + if (attachmentId != guid.ToString()) + throw new NotSupportedException($"{attachmentId} != {guid}"); } - // J Ouellette Added - internal string GetDataInsertDate(int toolTypeId, long headerId, string title) + + string IMetrologyRepository.GetDataInsertDate(int toolTypeId, long headerId, string title) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); @@ -560,7 +586,8 @@ public class MetrologyRepository : IMetrologyRepository return conn.ExecuteScalar(sql, param: new { HeaderID = headerId, Title = title }); } - internal void SetDataDirName(string tableName, long headerId, string title, string dateDir) + + void IMetrologyRepository.SetDataDirName(string tableName, long headerId, string title, string dateDir) { using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = @@ -568,13 +595,13 @@ public class MetrologyRepository : IMetrologyRepository _ = conn.Execute(sql, param: new { HeaderID = headerId, Title = title, AttachDirName = dateDir }); } - internal void PurgeExistingData(int toolTypeId, string title) + void IMetrologyRepository.PurgeExistingData(int toolTypeId, string title) { using DbConnection conn = _DBConnectionFactory.GetDbConnection(); _ = conn.Execute("PurgeExistingData", param: new { ToolTypeID = toolTypeId, Title = title }, commandType: CommandType.StoredProcedure); } - internal DataSet GetOIExportData(int toolTypeId, long headerid) + DataSet IMetrologyRepository.GetOIExportData(int toolTypeId, long headerid) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); @@ -626,7 +653,7 @@ public class MetrologyRepository : IMetrologyRepository return results.ToArray(); } - internal HeaderCommon[] GetHeaderTitles(int? toolTypeId, int? pageNo, int? pageSize, out long totalRecords) + HeaderCommon[] IMetrologyRepository.GetHeaderTitles(int? toolTypeId, int? pageNo, int? pageSize, out long totalRecords) { HeaderCommon[] headers; if (toolTypeId is not null && (pageNo is not null || pageSize is not null)) @@ -662,7 +689,7 @@ public class MetrologyRepository : IMetrologyRepository return headers; } - internal IEnumerable> GetHeaderFields(int toolTypeId, long headerid) + IEnumerable> IMetrologyRepository.GetHeaderFields(int toolTypeId, long headerid) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); @@ -689,7 +716,9 @@ public class MetrologyRepository : IMetrologyRepository if (dt.Rows.Count > 0) dr = dt.Rows[0]; - foreach (ToolTypeMetadata m in md.Where(m => m.Header == true && m.TableDisplayOrder > 0).OrderBy(m => m.TableDisplayOrder)) + foreach (ToolTypeMetadata m in md.Where(m => + m.Header == true && m.TableDisplayOrder > 0).OrderBy(m => + m.TableDisplayOrder)) { string v = ""; if (dr is not null) @@ -705,7 +734,7 @@ public class MetrologyRepository : IMetrologyRepository return r; } - internal IEnumerable GetAwaitingDisposition() + IEnumerable IMetrologyRepository.GetAwaitingDisposition() { IEnumerable? r; if (!string.IsNullOrEmpty(_MockRoot)) @@ -723,7 +752,7 @@ public class MetrologyRepository : IMetrologyRepository return r; } - internal int UpdateReviewDate(int toolTypeId, long headerId, bool clearDate) + int IMetrologyRepository.UpdateReviewDate(int toolTypeId, long headerId, bool clearDate) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); @@ -743,7 +772,7 @@ public class MetrologyRepository : IMetrologyRepository } } - internal Guid GetHeaderAttachmentIDByTitle(int toolTypeId, string title) + Guid IMetrologyRepository.GetHeaderAttachmentIDByTitle(int toolTypeId, string title) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); @@ -753,7 +782,7 @@ public class MetrologyRepository : IMetrologyRepository return conn.ExecuteScalar(sql, param: new { Title = title }); } - internal Guid GetDataAttachmentIDByTitle(int toolTypeId, string title) + Guid IMetrologyRepository.GetDataAttachmentIDByTitle(int toolTypeId, string title) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); @@ -763,31 +792,13 @@ public class MetrologyRepository : IMetrologyRepository return conn.ExecuteScalar(sql, param: new { Title = title }); } - DataTable IMetrologyRepository.GetDataSharePoint(int toolTypeId, string headerId) => throw new NotImplementedException(); - - IEnumerable IMetrologyRepository.GetToolTypes() => GetToolTypes(); - ToolType IMetrologyRepository.GetToolTypeByName(string name) => GetToolTypeByName(name); - ToolType IMetrologyRepository.GetToolTypeByID(int id) => GetToolTypeByID(id); - IEnumerable IMetrologyRepository.GetToolTypeMetadataByToolTypeID(int id) => GetToolTypeMetadataByToolTypeID(id); - TransactionScope IMetrologyRepository.StartTransaction() => StartTransaction(); - void IMetrologyRepository.PurgeExistingData(int toolTypeId, string title) => PurgeExistingData(toolTypeId, title); - long IMetrologyRepository.InsertToolDataJSON(JToken jsonbody, long headerId, List metaData, string tableName) => InsertToolDataJSON(jsonbody, headerId, metaData, tableName); - DataTable IMetrologyRepository.ExportData(string spName, DateTime startTime, DateTime endTime) => ExportData(spName, startTime, endTime); - DataTable IMetrologyRepository.GetHeaders(int toolTypeId, string? startTime, string? endTime, int? pageNo, int? pageSize, long? headerid, out long totalRecords) => GetHeaders(toolTypeId, startTime, endTime, pageNo, pageSize, headerid, out totalRecords); - DataTable IMetrologyRepository.GetData(int toolTypeId, long headerId) => GetData(toolTypeId, headerId); - HeaderCommon[] IMetrologyRepository.GetHeaderTitles(int? toolTypeId, int? pageNo, int? pageSize, out long totalRecords) => GetHeaderTitles(toolTypeId, pageNo, pageSize, out totalRecords); - Guid IMetrologyRepository.GetHeaderAttachmentIDByTitle(int toolTypeId, string title) => GetHeaderAttachmentIDByTitle(toolTypeId, title); - Guid IMetrologyRepository.GetDataAttachmentIDByTitle(int toolTypeId, string title) => GetDataAttachmentIDByTitle(toolTypeId, title); - Guid IMetrologyRepository.GetHeaderAttachmentID(int toolTypeId, long headerId) => GetHeaderAttachmentID(toolTypeId, headerId); - string IMetrologyRepository.GetHeaderInsertDate(int toolTypeId, long headerId) => GetHeaderInsertDate(toolTypeId, headerId); - string IMetrologyRepository.GetAttachmentInsertDateByGUID(string tableName, Guid attachmentId) => GetAttachmentInsertDateByGUID(tableName, attachmentId); - void IMetrologyRepository.SetHeaderDirName(string tableName, long headerId, string dateDir) => SetHeaderDirName(tableName, headerId, dateDir); - Guid IMetrologyRepository.GetDataAttachmentID(int toolTypeId, long headerId, string title) => GetDataAttachmentID(toolTypeId, headerId, title); - string IMetrologyRepository.GetDataInsertDate(int toolTypeId, long headerId, string title) => GetDataInsertDate(toolTypeId, headerId, title); - void IMetrologyRepository.SetDataDirName(string tableName, long headerId, string title, string dateDir) => SetDataDirName(tableName, headerId, title, dateDir); - DataSet IMetrologyRepository.GetOIExportData(int toolTypeId, long headerid) => GetOIExportData(toolTypeId, headerid); - IEnumerable> IMetrologyRepository.GetHeaderFields(int toolTypeId, long headerid) => GetHeaderFields(toolTypeId, headerid); - IEnumerable IMetrologyRepository.GetAwaitingDisposition() => GetAwaitingDisposition(); - int IMetrologyRepository.UpdateReviewDate(int toolTypeId, long headerId, bool clearDate) => UpdateReviewDate(toolTypeId, headerId, clearDate); + IEnumerable IMetrologyRepository.GetToolTypes() => + GetToolTypes(); + ToolType IMetrologyRepository.GetToolTypeByID(int id) => + GetToolTypeByID(id); + TransactionScope IMetrologyRepository.StartTransaction() => + StartTransaction(); + IEnumerable IMetrologyRepository.GetToolTypeMetadataByToolTypeID(int id) => + GetToolTypeMetadataByToolTypeID(id); } \ No newline at end of file diff --git a/Server/Services/AttachmentsService.cs b/Server/Services/AttachmentsService.cs index 9e34a64..f7f4f2a 100644 --- a/Server/Services/AttachmentsService.cs +++ b/Server/Services/AttachmentsService.cs @@ -39,7 +39,7 @@ public class AttachmentsService : IAttachmentsService HttpClient httpClient = _HttpClientFactory.CreateClient(); Uri mesaFileShareMetrologySi = new(_AppSettings.EcMesaFileShareMetrologySi); int weekNum = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(insertDate, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); - Uri uri = _FileShareRepository.Append(mesaFileShareMetrologySi, $"{tableName}_", year, $"WW{weekNum:00}", attachmentId.ToString(), filename); + Uri uri = _FileShareRepository.Append(mesaFileShareMetrologySi, "MetrologyAttachments", $"{tableName}_", year, $"WW{weekNum:00}", attachmentId.ToString(), filename); HttpResponseMessage httpResponseMessage = _FileShareRepository.ReadFile(httpClient, uri); if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK) throw new Exception("File not found!"); @@ -77,54 +77,49 @@ public class AttachmentsService : IAttachmentsService return GetAttachmentStream(tableName, attachmentId, filename); } - private void SaveAttachment(ToolType toolType, long headerId, string dataUniqueId, string filename, IFormFile uploadedFile) + void IAttachmentsService.SaveAttachment(ToolType toolType, Attachment attachment) { if (toolType is null) throw new Exception("Invalid tool type"); - + if (attachment.HeaderId is null) + throw new NullReferenceException($"{nameof(attachment.HeaderId)}"); + if (attachment.AttachmentId is null) + throw new NullReferenceException($"{nameof(attachment.AttachmentId)}"); + if (attachment.SourceFileName is null) + throw new NullReferenceException($"{nameof(attachment.SourceFileName)}"); + if (attachment.DestinationFileName is null) + throw new NullReferenceException($"{nameof(attachment.DestinationFileName)}"); + string? tableName; + DateTime insertDate; using System.Transactions.TransactionScope trans = _MetrologyRepository.StartTransaction(); - Guid attachmentId = Guid.Empty; - DateTime insertDate = new(); - string? tableName = ""; - if (string.IsNullOrWhiteSpace(dataUniqueId)) + if (string.IsNullOrWhiteSpace(attachment.UniqueId)) { - attachmentId = _MetrologyRepository.GetHeaderAttachmentID(toolType.ID, headerId); - insertDate = Convert.ToDateTime(_MetrologyRepository.GetHeaderInsertDate(toolType.ID, headerId)); + _MetrologyRepository.SetHeaderAttachmentID(toolType.ID, attachment.HeaderId.Value, attachment.AttachmentId); + insertDate = Convert.ToDateTime(_MetrologyRepository.GetHeaderInsertDate(toolType.ID, attachment.HeaderId.Value)); tableName = toolType.HeaderTableName; } else { - attachmentId = _MetrologyRepository.GetDataAttachmentID(toolType.ID, headerId, dataUniqueId); - insertDate = Convert.ToDateTime(_MetrologyRepository.GetDataInsertDate(toolType.ID, headerId, dataUniqueId)); - // Get Date for new directory name + _MetrologyRepository.SetDataAttachmentID(toolType.ID, attachment.HeaderId.Value, attachment.UniqueId, attachment.AttachmentId); + insertDate = Convert.ToDateTime(_MetrologyRepository.GetDataInsertDate(toolType.ID, attachment.HeaderId.Value, attachment.UniqueId)); tableName = toolType.DataTableName; } - if (Equals(attachmentId, Guid.Empty)) + if (Equals(attachment.AttachmentId, Guid.Empty)) { trans.Dispose(); throw new Exception("Invalid attachment ID!"); } - string year = insertDate.Year.ToString(); - HttpClient httpClient = _HttpClientFactory.CreateClient(); - Uri mesaFileShareMetrologySi = new(_AppSettings.EcMesaFileShareMetrologySi); + const string asdf = "\\asdf\asdf"; int weekNum = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(insertDate, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); - Uri uri = _FileShareRepository.Append(mesaFileShareMetrologySi, $"{tableName}_", year, $"WW{weekNum:00}", attachmentId.ToString(), filename); - HttpResponseMessage httpResponseMessage = _FileShareRepository.ReadFile(httpClient, uri); - if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK) + string checkPath = Path.Combine(asdf, $"{tableName}_", insertDate.Year.ToString(), $"WW{weekNum:00}", attachment.AttachmentId.ToString(), attachment.DestinationFileName); + if (!attachment.SourceFileName.EndsWith(checkPath[asdf.Length..])) { trans.Dispose(); throw new Exception("Invalid attachment path!"); } - uploadedFile.CopyTo(httpResponseMessage.Content.ReadAsStream()); trans.Complete(); } - void IAttachmentsService.SaveAttachment(ToolType toolType, long headerId, string dataUniqueId, string filename, object uploadedFile) - { - IFormFile formFile = (IFormFile)uploadedFile; - SaveAttachment(toolType, headerId, dataUniqueId, filename, formFile); - } - string? IAttachmentsService.GetProcessDataStandardFormat(IMetrologyRepository metrologyRepository, int toolTypeId, long headerId) { string? result; @@ -144,8 +139,8 @@ public class AttachmentsService : IAttachmentsService { year = dateTime.Year.ToString(); weekNum = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); - weekDirectory = _FileShareRepository.Append(mesaFileShareMetrologySi, $"{toolType.HeaderTableName}_", year, $"WW{weekNum:00}"); - checkDirectory = _FileShareRepository.Append(weekDirectory, headerId.ToString()); + weekDirectory = _FileShareRepository.Append(mesaFileShareMetrologySi, "MetrologyAttachments", $"{toolType.HeaderTableName}_", year, $"WW{weekNum:00}"); + checkDirectory = _FileShareRepository.Append(weekDirectory, $"-{headerId}"); httpResponseMessage = httpClient.GetAsync(checkDirectory); httpResponseMessage.Wait(); if (httpResponseMessage.Result.StatusCode != System.Net.HttpStatusCode.OK) diff --git a/Shared/Models/Attachment.cs b/Shared/Models/Attachment.cs new file mode 100644 index 0000000..fced024 --- /dev/null +++ b/Shared/Models/Attachment.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; + +namespace OI.Metrology.Shared.Models; + +public record Attachment(string? SubGroupId, + long? HeaderId, + string? HeaderIdDirectory, + string? UniqueId, + string? DestinationFileName, + string? SourceFileName, + string? AttachmentId); + +[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] +[JsonSerializable(typeof(Attachment))] +public partial class AttachmentSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Shared/Models/Stateless/IFileShareController.cs b/Shared/Models/Stateless/IFileShareController.cs index 1b35053..13c2e63 100644 --- a/Shared/Models/Stateless/IFileShareController.cs +++ b/Shared/Models/Stateless/IFileShareController.cs @@ -6,7 +6,9 @@ public interface IFileShareController enum Action : int { Index = 0, - MarkAsPinned = 1 + CopyFile = 1, + MoveFile = 2, + FileWrite = 3, } static string GetRouteName() => nameof(IFileShareController)[1..^10]; diff --git a/Shared/Models/Stateless/IFileShareRepository.cs b/Shared/Models/Stateless/IFileShareRepository.cs index ef07d96..2bf20b0 100644 --- a/Shared/Models/Stateless/IFileShareRepository.cs +++ b/Shared/Models/Stateless/IFileShareRepository.cs @@ -10,7 +10,6 @@ public interface IFileShareRepository HttpResponseMessage ReadFile(HttpClient httpClient, Uri uri); void CopyFile(HttpClient httpClient, string from, string to); void MoveFile(HttpClient httpClient, string from, string to); - void FileWrite(HttpClient httpClient, string path, string contents); List GetNginxFileSystemSortableCollection(HttpClient httpClient, Uri uri, string? endsWith); } \ No newline at end of file diff --git a/Shared/Models/Stateless/IInboundController.cs b/Shared/Models/Stateless/IInboundController.cs index bec0f61..b6238b6 100644 --- a/Shared/Models/Stateless/IInboundController.cs +++ b/Shared/Models/Stateless/IInboundController.cs @@ -1,3 +1,5 @@ +using System.Text.Json; + namespace OI.Metrology.Shared.Models.Stateless; public interface IInboundController @@ -9,7 +11,7 @@ public interface IInboundController } static string GetRouteName() => nameof(IInboundController)[1..^10]; - T Post(string tooltype); - T AttachFile(string tooltype, long headerid, string datauniqueid = ""); + T Post(string tooltype, JsonElement? jsonElement); + T AttachFile(string tooltype, Attachment? attachment); } \ No newline at end of file diff --git a/Shared/Models/Stateless/IInboundRepository.cs b/Shared/Models/Stateless/IInboundRepository.cs index 7ddfdda..e95422e 100644 --- a/Shared/Models/Stateless/IInboundRepository.cs +++ b/Shared/Models/Stateless/IInboundRepository.cs @@ -1,5 +1,6 @@ using OI.Metrology.Shared.Services; using System.Net; +using System.Text.Json; namespace OI.Metrology.Shared.Models.Stateless; @@ -7,7 +8,7 @@ public interface IInboundRepository { bool IsIPAddressAllowed(string inboundApiAllowedIPList, IPAddress? remoteIP); - DataResponse Data(IMetrologyRepository metrologyRepository, IInboundDataService inboundDataService, string tooltype, string? json); - string? AttachFile(IMetrologyRepository metrologyRepository, IAttachmentsService attachmentsService, string tooltype, long headerid, string datauniqueid, string fileName, object uploadedFile); + void AttachFile(IMetrologyRepository metrologyRepository, IAttachmentsService attachmentsService, string toolTypeName, Attachment? attachment); + DataResponse Data(IMetrologyRepository metrologyRepository, IInboundDataService inboundDataService, string toolTypeName, JsonElement? jsonElement); } \ No newline at end of file diff --git a/Shared/Models/Stateless/IInfinityQSV4Controller.cs b/Shared/Models/Stateless/IInfinityQSV4Controller.cs index de703a9..5106916 100644 --- a/Shared/Models/Stateless/IInfinityQSV4Controller.cs +++ b/Shared/Models/Stateless/IInfinityQSV4Controller.cs @@ -9,12 +9,15 @@ public interface IInfinityQSV4Controller MarkAsPinned = 1 } - static string GetRouteName() => nameof(IInfinityQSV4Controller)[1..^10]; + static string GetRouteName() => nameof(IInfinityQSV4Controller)[1..^12]; + static string GetRouteVersion() => nameof(IInfinityQSV4Controller)[1..^10][^2..]; + T GetData(string sub_group_id); T GetHeader(string sub_group_id); T GetDescriptors(string sub_group_id); T GetEpiProTempVerification(int[] night); T GetEpiProTempVerificationRows(int[] night); + T GetLastGroupIdWithValue(string process, string? part, int? test); T GetProductDataAverageSumOfDefectsProcessMeanProcessSigma(string process, string? recipe); T GetCommandText(string sub_group_id, string process, string job, string part, string lot, string date_time); diff --git a/Shared/Models/Stateless/IInfinityQSV4Repository.cs b/Shared/Models/Stateless/IInfinityQSV4Repository.cs index cb6b120..0b21f5d 100644 --- a/Shared/Models/Stateless/IInfinityQSV4Repository.cs +++ b/Shared/Models/Stateless/IInfinityQSV4Repository.cs @@ -17,6 +17,7 @@ public interface IInfinityQSV4Repository string GetCommandText(List eppReactorNumbers); List GetEpiProTempVerificationRows(int[] night); Result GetDescriptors(string subGroupId); + string GetLastGroupIdWithValue(string process, string? part, int? test); string GetProductDataAverageSumOfDefectsProcessMeanProcessSigma(string process, string? recipe); string GetCommandText(string? subGroupId, string? process, string? job, string? part, string? lot, string? dateTime); diff --git a/Shared/Models/Stateless/IMetrologyRepository.cs b/Shared/Models/Stateless/IMetrologyRepository.cs index 1d99b16..259a7d3 100644 --- a/Shared/Models/Stateless/IMetrologyRepository.cs +++ b/Shared/Models/Stateless/IMetrologyRepository.cs @@ -8,42 +8,30 @@ namespace OI.Metrology.Shared.Models.Stateless; public interface IMetrologyRepository { + ToolType GetToolTypeByID(int id); + TransactionScope StartTransaction(); IEnumerable GetToolTypes(); ToolType GetToolTypeByName(string name); - ToolType GetToolTypeByID(int id); - - IEnumerable GetToolTypeMetadataByToolTypeID(int id); - - TransactionScope StartTransaction(); - - void PurgeExistingData(int toolTypeId, string title); - long InsertToolDataJSON(JToken jsonbody, long headerId, List metaData, string tableName); - - DataTable ExportData(string spName, DateTime startTime, DateTime endTime); - - DataTable GetHeaders(int toolTypeId, string? startTime, string? endTime, int? pageNo, int? pageSize, long? headerid, out long totalRecords); DataTable GetData(int toolTypeId, long headerId); - DataTable GetDataSharePoint(int toolTypeId, string headerId); - - HeaderCommon[] GetHeaderTitles(int? toolTypeId, int? pageNo, int? pageSize, out long totalRecords); - - Guid GetHeaderAttachmentIDByTitle(int toolTypeId, string title); - Guid GetDataAttachmentIDByTitle(int toolTypeId, string title); - - Guid GetHeaderAttachmentID(int toolTypeId, long headerId); - string GetHeaderInsertDate(int toolTypeId, long headerId); - string GetAttachmentInsertDateByGUID(string tableName, Guid attachmentId); - void SetHeaderDirName(string tableName, long headerId, string dateDir); - Guid GetDataAttachmentID(int toolTypeId, long headerId, string title); - string GetDataInsertDate(int toolTypeId, long headerId, string title); - - void SetDataDirName(string tableName, long headerId, string title, string dateDir); + void PurgeExistingData(int toolTypeId, string title); DataSet GetOIExportData(int toolTypeId, long headerid); - - IEnumerable> GetHeaderFields(int toolTypeId, long headerid); - + Guid GetHeaderAttachmentID(int toolTypeId, long headerId); IEnumerable GetAwaitingDisposition(); - + string GetHeaderInsertDate(int toolTypeId, long headerId); + Guid GetDataAttachmentIDByTitle(int toolTypeId, string title); + Guid GetHeaderAttachmentIDByTitle(int toolTypeId, string title); int UpdateReviewDate(int toolTypeId, long headerId, bool clearDate); + IEnumerable GetToolTypeMetadataByToolTypeID(int id); + string GetDataInsertDate(int toolTypeId, long headerId, string title); + void SetHeaderDirName(string tableName, long headerId, string dateDir); + DataTable ExportData(string spName, DateTime startTime, DateTime endTime); + string GetAttachmentInsertDateByGUID(string tableName, Guid attachmentId); + void SetHeaderAttachmentID(int toolTypeId, long headerId, string attachmentId); + void SetDataDirName(string tableName, long headerId, string title, string dateDir); + IEnumerable> GetHeaderFields(int toolTypeId, long headerid); + void SetDataAttachmentID(int toolTypeId, long headerId, string title, string attachmentId); + HeaderCommon[] GetHeaderTitles(int? toolTypeId, int? pageNo, int? pageSize, out long totalRecords); + long InsertToolDataJSON(JToken jsonbody, long headerId, List metaData, string tableName); + DataTable GetHeaders(int toolTypeId, string? startTime, string? endTime, int? pageNo, int? pageSize, long? headerid, out long totalRecords); } \ No newline at end of file diff --git a/Shared/Services/IAttachmentsService.cs b/Shared/Services/IAttachmentsService.cs index ceaf20a..f7d624f 100644 --- a/Shared/Services/IAttachmentsService.cs +++ b/Shared/Services/IAttachmentsService.cs @@ -1,6 +1,7 @@ namespace OI.Metrology.Shared.Services; using DataModels; +using OI.Metrology.Shared.Models; using OI.Metrology.Shared.Models.Stateless; public interface IAttachmentsService @@ -8,5 +9,5 @@ public interface IAttachmentsService Stream GetAttachmentStreamByTitle(ToolType toolType, bool header, string title, string filename); Stream GetAttachmentStreamByAttachmentId(ToolType toolType, bool header, Guid attachmentId, string filename); string? GetProcessDataStandardFormat(IMetrologyRepository metrologyRepository, int toolTypeId, long headerId); - void SaveAttachment(ToolType toolType, long headerId, string dataUniqueId, string filename, object uploadedFile); + void SaveAttachment(ToolType toolType, Attachment attachment); } \ No newline at end of file diff --git a/Tests/.vscode/tasks.json b/Tests/.vscode/tasks.json index b94d8dd..e86b351 100644 --- a/Tests/.vscode/tasks.json +++ b/Tests/.vscode/tasks.json @@ -13,6 +13,32 @@ ], "problemMatcher": "$msCompile" }, + { + "label": "testDebug", + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/OI.Metrology.Tests.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "testRelease", + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/OI.Metrology.Tests.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary", + "-c", + "Release" + ], + "problemMatcher": "$msCompile" + }, { "label": "Format", "command": "dotnet", @@ -28,64 +54,35 @@ ], "problemMatcher": "$msCompile" }, + { + "label": "Format-Whitespace", + "command": "dotnet", + "type": "process", + "args": [ + "format", + "whitespace" + ], + "problemMatcher": "$msCompile" + }, { "label": "publish", "command": "dotnet", "type": "process", "args": [ "publish", - "${workspaceFolder}/OI.Metrology.Tests.csproj", + "${workspaceFolder}/../Server/OI.Metrology.Server.csproj", "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" + "/consoleloggerparameters:NoSummary", + "--configuration", + "Release", + "--runtime", + "win-x64", + "--self-contained", + "-o", + "D:/web-sites/OI-Metrology/hh-3498d1da-_______-OI-Metrology-Release/Server" ], "problemMatcher": "$msCompile" }, - { - "label": "old-watch", - "command": "dotnet", - "type": "process", - "args": [ - "watch", - "run", - "--project", - "${workspaceFolder}/OI.Metrology.Tests.csproj" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "watch", - "command": "dotnet", - "type": "process", - "args": [ - "watch", - "--launch-profile", - "http", - "run", - "--project", - "${workspaceFolder}/OI.Metrology.Tests.csproj", - "--verbose" - ], - "isBackground": true, - "dependsOn": [ - "build" - ], - "problemMatcher": { - "fileLocation": "relative", - "pattern": { - "regexp": "^([^\\s].*)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\):\\s+(error|warning|info)\\s+(TS\\d+)\\s*:\\s*(.*)$", - "file": 1, - "location": 2, - "severity": 3, - "code": 4, - "message": 5 - }, - "background": { - "activeOnStart": true, - "beginsPattern": "^.*Shutdown requested.*", - "endsPattern": "^.*Application started.*" - } - } - }, { "label": "File-Folder-Helper AOT s V Repositories", "type": "shell", diff --git a/Tests/UnitTestInfinityQSV4Controller.cs b/Tests/UnitTestInfinityQSV4Controller.cs index 59c606a..9460c22 100644 --- a/Tests/UnitTestInfinityQSV4Controller.cs +++ b/Tests/UnitTestInfinityQSV4Controller.cs @@ -16,6 +16,7 @@ public class UnitTestInfinityQSV4Controller private static ILogger? _Logger; private static string _ControllerName; private static TestContext _TestContext; + private static string _ControllerVersion; private static WebApplicationFactory? _WebApplicationFactory; #pragma warning restore @@ -27,7 +28,8 @@ public class UnitTestInfinityQSV4Controller _WebApplicationFactory = new WebApplicationFactory(); IServiceProvider serviceProvider = _WebApplicationFactory.Services.CreateScope().ServiceProvider; _Logger = serviceProvider.GetRequiredService>(); - _ControllerName = nameof(Server.ApiControllers.InfinityQSV4Controller)[..^10]; + _ControllerName = nameof(Server.ApiControllers.InfinityQSV4Controller)[..^12]; + _ControllerVersion = nameof(Server.ApiControllers.InfinityQSV4Controller)[..^10][^2..]; } private static void NonThrowTryCatch() @@ -42,6 +44,7 @@ public class UnitTestInfinityQSV4Controller { _Logger?.LogInformation("Starting Web Application"); Assert.AreEqual(IInfinityQSV4Controller.GetRouteName(), _ControllerName); + Assert.AreEqual(IInfinityQSV4Controller.GetRouteVersion(), _ControllerVersion); _Logger?.LogInformation("{TestName} completed", _TestContext?.TestName); NonThrowTryCatch(); } @@ -64,7 +67,7 @@ public class UnitTestInfinityQSV4Controller HttpClient? httpClient = _WebApplicationFactory?.CreateClient(); _Logger?.LogInformation("Starting Web Application"); Assert.IsTrue(httpClient is not null); - string? json = await httpClient.GetStringAsync($"api/{_ControllerName}/1698497987/commandText/?process=61&job=CDE5&part=5012&lot=575908&date_time=2023-02-24 15:15:00"); + string? json = await httpClient.GetStringAsync($"api/{_ControllerVersion}/{_ControllerName}/1698497987/commandText/?process=61&job=CDE5&part=5012&lot=575908&date_time=2023-02-24 15:15:00"); File.WriteAllText(Path.Combine(AppContext.BaseDirectory, $"{_ControllerName}-{nameof(GetCommandText)}.sql"), json); Assert.IsNotNull(json); _Logger?.LogInformation("{TestName} completed", _TestContext?.TestName); @@ -95,8 +98,8 @@ public class UnitTestInfinityQSV4Controller HttpClient? httpClient = _WebApplicationFactory?.CreateClient(); _Logger?.LogInformation("Starting Web Application"); Assert.IsTrue(httpClient is not null); - //string? json = await httpClient.GetStringAsync($"api/{_ControllerName}/1698497987 575908_2023-02-24 14-18-05.txt/data"); - string? json = await httpClient.GetStringAsync($"api/{_ControllerName}/1698497987/data"); + //string? json = await httpClient.GetStringAsync($"api/{_ControllerVersion}/{_ControllerName}/1698497987 575908_2023-02-24 14-18-05.txt/data"); + string? json = await httpClient.GetStringAsync($"api/{_ControllerVersion}/{_ControllerName}/1698497987/data"); File.WriteAllText(Path.Combine(AppContext.BaseDirectory, $"{_ControllerName}-{nameof(GetData)}.json"), json); Result? result = System.Text.Json.JsonSerializer.Deserialize>(json); Assert.IsNotNull(result?.Results); @@ -125,8 +128,8 @@ public class UnitTestInfinityQSV4Controller HttpClient? httpClient = _WebApplicationFactory?.CreateClient(); _Logger?.LogInformation("Starting Web Application"); Assert.IsTrue(httpClient is not null); - //string? json = await httpClient.GetStringAsync($"api/{_ControllerName}/1698497987 575908_2023-02-24 14-18-05.txt/descriptors"); - string? json = await httpClient.GetStringAsync($"api/{_ControllerName}/1698497987/descriptors"); + //string? json = await httpClient.GetStringAsync($"api/{_ControllerVersion}/{_ControllerName}/1698497987 575908_2023-02-24 14-18-05.txt/descriptors"); + string? json = await httpClient.GetStringAsync($"api/{_ControllerVersion}/{_ControllerName}/1698497987/descriptors"); File.WriteAllText(Path.Combine(AppContext.BaseDirectory, $"{_ControllerName}-{nameof(GetDescriptors)}.json"), json); Result? result = System.Text.Json.JsonSerializer.Deserialize>(json); Assert.IsNotNull(result?.Results); @@ -153,7 +156,7 @@ public class UnitTestInfinityQSV4Controller HttpClient? httpClient = _WebApplicationFactory?.CreateClient(); _Logger?.LogInformation("Starting Web Application"); Assert.IsTrue(httpClient is not null); - string? json = await httpClient.GetStringAsync($"api/{_ControllerName}/1698497987/header"); + string? json = await httpClient.GetStringAsync($"api/{_ControllerVersion}/{_ControllerName}/1698497987/header"); File.WriteAllText(Path.Combine(AppContext.BaseDirectory, $"{_ControllerName}-{nameof(GetHeader)}.json"), json); Result? result = System.Text.Json.JsonSerializer.Deserialize>(json); Assert.IsNotNull(result?.Results); @@ -180,7 +183,7 @@ public class UnitTestInfinityQSV4Controller HttpClient? httpClient = _WebApplicationFactory?.CreateClient(); _Logger?.LogInformation("Starting Web Application"); Assert.IsTrue(httpClient is not null); - string? result = await httpClient.GetStringAsync($"api/{_ControllerName}/41/product-data-average-sum-of-defects-process-mean-process-sigma?recipe=8IN_THIN ROTR"); + string? result = await httpClient.GetStringAsync($"api/{_ControllerVersion}/{_ControllerName}/41/product-data-average-sum-of-defects-process-mean-process-sigma?recipe=8IN_THIN ROTR"); File.WriteAllText(Path.Combine(AppContext.BaseDirectory, $"{_ControllerName}-{nameof(GetProductDataAverageSumOfDefectsProcessMeanProcessSigma)}.json"), result); Assert.IsNotNull(result); _Logger?.LogInformation("{TestName} completed", _TestContext?.TestName); @@ -206,7 +209,7 @@ public class UnitTestInfinityQSV4Controller HttpClient? httpClient = _WebApplicationFactory?.CreateClient(); _Logger?.LogInformation("Starting Web Application"); Assert.IsTrue(httpClient is not null); - string? result = await httpClient.GetStringAsync($"api/{_ControllerName}/epi-pro-temp-verification-collection/?night=44&night=46&night=52&night=54"); + string? result = await httpClient.GetStringAsync($"api/{_ControllerVersion}/{_ControllerName}/epi-pro-temp-verification-collection/?night=44&night=46&night=52&night=54"); File.WriteAllText(Path.Combine(AppContext.BaseDirectory, $"{_ControllerName}-{nameof(GetEpiProTempVerificationRows)}.json"), result); Assert.IsNotNull(result); _Logger?.LogInformation("{TestName} completed", _TestContext?.TestName); @@ -232,7 +235,7 @@ public class UnitTestInfinityQSV4Controller HttpClient? httpClient = _WebApplicationFactory?.CreateClient(); _Logger?.LogInformation("Starting Web Application"); Assert.IsTrue(httpClient is not null); - string? result = await httpClient.GetStringAsync($"api/{_ControllerName}/epi-pro-temp-verification/?night=44&night=46&night=52&night=54"); + string? result = await httpClient.GetStringAsync($"api/{_ControllerVersion}/{_ControllerName}/epi-pro-temp-verification/?night=44&night=46&night=52&night=54"); File.WriteAllText(Path.Combine(AppContext.BaseDirectory, $"{_ControllerName}-{nameof(GetEpiProTempVerification)}.html"), result); Assert.IsNotNull(result); _Logger?.LogInformation("{TestName} completed", _TestContext?.TestName); @@ -295,4 +298,35 @@ public class UnitTestInfinityQSV4Controller NonThrowTryCatch(); } + [TestMethod] + public void GetLastGroupIdWithValue() + { + const string part = "4610"; + const int test = 1228920625; + const string process = "35"; + _Logger?.LogInformation("Starting Web Application"); + IServiceProvider? serviceProvider = _WebApplicationFactory?.Services.CreateScope().ServiceProvider; + IInfinityQSV4Repository? infinityQSV4Repository = serviceProvider?.GetRequiredService(); + string? result = infinityQSV4Repository?.GetLastGroupIdWithValue(process, part, test); + Assert.IsNotNull(result); + _Logger?.LogInformation("{TestName} completed", _TestContext?.TestName); + NonThrowTryCatch(); + } + + [TestMethod] + public async Task GetLastGroupIdWithValueApi() + { + const string part = "4610"; + const int test = 1228920625; + const string process = "35"; + HttpClient? httpClient = _WebApplicationFactory?.CreateClient(); + _Logger?.LogInformation("Starting Web Application"); + Assert.IsTrue(httpClient is not null); + string? result = await httpClient.GetStringAsync($"api/{_ControllerVersion}/{_ControllerName}/{process}/last-group-id-with-value/?part={part}&test={test}"); + File.WriteAllText(Path.Combine(AppContext.BaseDirectory, $"{_ControllerName}-{nameof(GetLastGroupIdWithValue)}.html"), result); + Assert.IsNotNull(result); + _Logger?.LogInformation("{TestName} completed", _TestContext?.TestName); + NonThrowTryCatch(); + } + } \ No newline at end of file diff --git a/Wafer-Counter/.vscode/format-report.json b/Wafer-Counter/.vscode/format-report.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/Wafer-Counter/.vscode/format-report.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/Wafer-Counter/.vscode/launch.json b/Wafer-Counter/.vscode/launch.json new file mode 100644 index 0000000..9937495 --- /dev/null +++ b/Wafer-Counter/.vscode/launch.json @@ -0,0 +1,38 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/bin/Debug/net8.0/OI.Metrology.Wafer.Counter.dll", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)", + "uriFormat": "%s/swagger/index.html" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "preLaunchTask": "watch", + "processName": "OI.Metrology.Wafer.Counter.exe" + } + ] +} \ No newline at end of file diff --git a/Wafer-Counter/.vscode/settings.json b/Wafer-Counter/.vscode/settings.json new file mode 100644 index 0000000..9f80e14 --- /dev/null +++ b/Wafer-Counter/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "Infineon" + ] +} \ No newline at end of file diff --git a/Wafer-Counter/.vscode/tasks.json b/Wafer-Counter/.vscode/tasks.json new file mode 100644 index 0000000..6bcb3a2 --- /dev/null +++ b/Wafer-Counter/.vscode/tasks.json @@ -0,0 +1,163 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/OI.Metrology.Wafer.Counter.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "buildTests", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/../Tests/OI.Metrology.Tests.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "testDebug", + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/../Tests/OI.Metrology.Tests.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "testRelease", + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/../Tests/OI.Metrology.Tests.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary", + "-c", + "Release" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "Format", + "command": "dotnet", + "type": "process", + "args": [ + "format", + "--report", + ".vscode", + "--verbosity", + "detailed", + "--severity", + "warn" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "Format-Whitespace", + "command": "dotnet", + "type": "process", + "args": [ + "format", + "whitespace" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/OI.Metrology.Wafer.Counter.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary", + "--configuration", + "Release", + "--runtime", + "win-x64", + "--self-contained", + "-o", + "D:/web-sites/OI-Metrology/oo-8e5dd137-_______-OI-Metrology-Release/Wafer-Counter" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "Publish AOT", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/OI.Metrology.Wafer.Counter.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary", + "--configuration", + "Release", + "--runtime", + "win-x64", + "-p:PublishAot=true", + "/property:GenerateFullPaths=true" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "old-watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/OI.Metrology.Wafer.Counter.csproj" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "--launch-profile", + "http", + "run", + "--project", + "${workspaceFolder}/OI.Metrology.Wafer.Counter.csproj", + "--verbose" + ], + "isBackground": true, + "dependsOn": [ + "build" + ], + "problemMatcher": { + "fileLocation": "relative", + "pattern": { + "regexp": "^([^\\s].*)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\):\\s+(error|warning|info)\\s+(TS\\d+)\\s*:\\s*(.*)$", + "file": 1, + "location": 2, + "severity": 3, + "code": 4, + "message": 5 + }, + "background": { + "activeOnStart": true, + "beginsPattern": "^.*Shutdown requested.*", + "endsPattern": "^.*Application started.*" + } + } + } + ] +} \ No newline at end of file diff --git a/Wafer-Counter/ApiControllers/AppSettingsController.cs b/Wafer-Counter/ApiControllers/AppSettingsController.cs new file mode 100644 index 0000000..d6a4411 --- /dev/null +++ b/Wafer-Counter/ApiControllers/AppSettingsController.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Mvc; +using OI.Metrology.Shared.Models.Stateless; + +namespace OI.Metrology.Wafer.Counter.ApiControllers; + +[ApiController] +[Route("api/[controller]")] +public class AppSettingsController : ControllerBase, IAppSettingsController +{ + + private readonly IAppSettingsRepository _AppSettingsRepository; + + public AppSettingsController(IAppSettingsRepository AppSettingsRepository) => + _AppSettingsRepository = AppSettingsRepository; + + [HttpGet(nameof(IAppSettingsController.Action.App))] + public ActionResult GetAppSettings() => + Ok(_AppSettingsRepository.GetAppSettings()); + + [HttpGet(nameof(IAppSettingsController.Action.DevOps))] + public ActionResult GetBuildNumberAndGitCommitSeven() => + Ok(_AppSettingsRepository.GetBuildNumberAndGitCommitSeven()); + +} \ No newline at end of file diff --git a/Wafer-Counter/ApiControllers/FileShareController.cs b/Wafer-Counter/ApiControllers/FileShareController.cs new file mode 100644 index 0000000..2b299c8 --- /dev/null +++ b/Wafer-Counter/ApiControllers/FileShareController.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Mvc; +using OI.Metrology.Shared.Models.Stateless; + +namespace OI.Metrology.Wafer.Counter.ApiControllers; + +[Route("api/v1/file-share")] +public class FileShareController : Controller, IFileShareController +{ + + private readonly IFileShareRepository _FileShareRepository; + + public FileShareController(IFileShareRepository fileShareRepository) => + _FileShareRepository = fileShareRepository; + + [HttpGet("copy-file")] + public IResult CopyFile(string from, string to) + { + _FileShareRepository.CopyFile(from, to); + return Results.Ok(); + } + + [HttpGet("move-file")] + public IResult MoveFile(string from, string to) + { + _FileShareRepository.MoveFile(from, to); + return Results.Ok(); + } + + [HttpGet("file-write")] + public IResult FileWrite(string path, string contents) + { + _FileShareRepository.FileWrite(path, contents); + return Results.Ok(); + } + +} \ No newline at end of file diff --git a/Server/ApiControllers/WaferCounterController.cs b/Wafer-Counter/ApiControllers/WaferCounterController.cs similarity index 92% rename from Server/ApiControllers/WaferCounterController.cs rename to Wafer-Counter/ApiControllers/WaferCounterController.cs index 893ee26..f610166 100644 --- a/Server/ApiControllers/WaferCounterController.cs +++ b/Wafer-Counter/ApiControllers/WaferCounterController.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using OI.Metrology.Shared.Models.Stateless; -namespace OI.Metrology.Server.ApiControllers; +namespace OI.Metrology.Wafer.Counter.ApiControllers; [Route("api/v1/[controller]")] public class WaferCounterController : Controller, IWaferCounterController diff --git a/Wafer-Counter/Models/AppSettings.cs b/Wafer-Counter/Models/AppSettings.cs new file mode 100644 index 0000000..20ed81f --- /dev/null +++ b/Wafer-Counter/Models/AppSettings.cs @@ -0,0 +1,27 @@ +using System.Text.Json; + +namespace OI.Metrology.Wafer.Counter.Models; + +public record AppSettings(string BuildNumber, + string Company, + string EcCharacterizationSi, + string EcMesaFileShareCharacterizationSi, + string GitCommitSeven, + bool IsDevelopment, + bool IsStaging, + string MockRoot, + string MonAResource, + string MonASite, + string URLs, + string WaferCounterDestinationDirectory, + int WaferCounterTwoFileSecondsWait, + string WorkingDirectoryName) +{ + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + +} \ No newline at end of file diff --git a/Wafer-Counter/Models/Binder/.editorconfig b/Wafer-Counter/Models/Binder/.editorconfig new file mode 100644 index 0000000..1c444cd --- /dev/null +++ b/Wafer-Counter/Models/Binder/.editorconfig @@ -0,0 +1,2 @@ +[*.cs] +csharp_preserve_single_line_statements = true \ No newline at end of file diff --git a/Wafer-Counter/Models/Binder/AppSettings.cs b/Wafer-Counter/Models/Binder/AppSettings.cs new file mode 100644 index 0000000..586f742 --- /dev/null +++ b/Wafer-Counter/Models/Binder/AppSettings.cs @@ -0,0 +1,99 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace OI.Metrology.Wafer.Counter.Models.Binder; + +public class AppSettings +{ + + public string? BuildNumber { get; set; } + public string? Company { get; set; } + public string? EcCharacterizationSi { get; set; } + public string? EcMesaFileShareCharacterizationSi { get; set; } + public string? GitCommitSeven { get; set; } + public bool? IsDevelopment { get; set; } + public bool? IsStaging { get; set; } + public string? MockRoot { get; set; } + public string? MonAResource { get; set; } + public string? MonASite { get; set; } + public string? URLs { get; set; } + public string? WaferCounterDestinationDirectory { get; set; } + public int? WaferCounterTwoFileSecondsWait { get; set; } + public string? WorkingDirectoryName { get; set; } + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + + private static void PreVerify(IConfigurationRoot configurationRoot, AppSettings? appSettings) + { + if (appSettings?.Company is null) + { + List paths = new(); + foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers) + { + if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider) + continue; + if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider) + continue; + paths.Add(physicalFileProvider.Root); + } + throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}"); + } + } + + private static Models.AppSettings Get(AppSettings? appSettings) + { + Models.AppSettings result; + if (appSettings is null) throw new NullReferenceException(nameof(appSettings)); + 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)); + if (appSettings.EcMesaFileShareCharacterizationSi is null) throw new NullReferenceException(nameof(EcMesaFileShareCharacterizationSi)); + if (appSettings.GitCommitSeven is null) throw new NullReferenceException(nameof(GitCommitSeven)); + if (appSettings.IsDevelopment is null) throw new NullReferenceException(nameof(IsDevelopment)); + if (appSettings.IsStaging is null) throw new NullReferenceException(nameof(IsStaging)); + if (appSettings.MockRoot is null) throw new NullReferenceException(nameof(MockRoot)); + if (appSettings.MonAResource is null) throw new NullReferenceException(nameof(MonAResource)); + if (appSettings.MonASite is null) throw new NullReferenceException(nameof(MonASite)); + if (appSettings.URLs is null) throw new NullReferenceException(nameof(URLs)); + 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, + appSettings.Company, + appSettings.EcCharacterizationSi, + appSettings.EcMesaFileShareCharacterizationSi, + appSettings.GitCommitSeven, + appSettings.IsDevelopment.Value, + appSettings.IsStaging.Value, + appSettings.MockRoot, + appSettings.MonAResource, + appSettings.MonASite, + appSettings.URLs, + appSettings.WaferCounterDestinationDirectory, + appSettings.WaferCounterTwoFileSecondsWait.Value, + appSettings.WorkingDirectoryName); + return result; + } + + public static Models.AppSettings Get(IConfigurationRoot configurationRoot) + { + Models.AppSettings result; +#pragma warning disable IL3050, IL2026 + AppSettings? appSettings = configurationRoot.Get(); +#pragma warning restore IL3050, IL2026 + PreVerify(configurationRoot, appSettings); + result = Get(appSettings); + return result; + } + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(AppSettings))] +internal partial class BinderAppSettingsSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Wafer-Counter/OI.Metrology.Wafer.Counter.csproj b/Wafer-Counter/OI.Metrology.Wafer.Counter.csproj new file mode 100644 index 0000000..a636b0a --- /dev/null +++ b/Wafer-Counter/OI.Metrology.Wafer.Counter.csproj @@ -0,0 +1,38 @@ + + + SAK + SAK + SAK + SAK + + + true + enable + false + enable + Exe + win-x64;linux-x64 + net8.0 + 2a0acd34-8f61-47a3-8818-73fa8fe04902 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Wafer-Counter/Program.cs b/Wafer-Counter/Program.cs new file mode 100644 index 0000000..539e865 --- /dev/null +++ b/Wafer-Counter/Program.cs @@ -0,0 +1,90 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Hosting.WindowsServices; +using OI.Metrology.Wafer.Counter.Models; +using OI.Metrology.Wafer.Counter.Repository; +using OI.Metrology.Shared.Models.Stateless; + +namespace OI.Metrology.Wafer.Counter; + +public class Program +{ + + public static int Main(string[] args) + { + ILogger? logger = null; + WebApplicationBuilder webApplicationBuilder = WebApplication.CreateBuilder(args); + _ = webApplicationBuilder.Configuration.AddUserSecrets(); + AppSettings appSettings = Models.Binder.AppSettings.Get(webApplicationBuilder.Configuration); + if (string.IsNullOrEmpty(appSettings.Company)) + throw new Exception("Company name must have a value!"); + if (string.IsNullOrEmpty(appSettings.WorkingDirectoryName)) + throw new Exception("Working directory name must have a value!"); + try + { + _ = webApplicationBuilder.Services.AddMemoryCache(); + _ = webApplicationBuilder.Services.Configure(options => options.SuppressModelStateInvalidFilter = true); + _ = webApplicationBuilder.Services.AddControllers(); + _ = webApplicationBuilder.Services.AddDistributedMemoryCache(); + + AppSettingsRepository appSettingsRepository = new(appSettings); + + _ = webApplicationBuilder.Services.AddHttpClient(); + _ = webApplicationBuilder.Services.AddSingleton(_ => appSettings); + _ = webApplicationBuilder.Services.AddSingleton(); + _ = webApplicationBuilder.Services.AddSingleton(); + _ = webApplicationBuilder.Services.AddSingleton>(_ => appSettingsRepository); + + _ = webApplicationBuilder.Services.AddSwaggerGen(); + _ = webApplicationBuilder.Services.AddSession(sessionOptions => + { + sessionOptions.IdleTimeout = TimeSpan.FromSeconds(2000); + sessionOptions.Cookie.HttpOnly = true; + sessionOptions.Cookie.IsEssential = true; + } + ); + if (WindowsServiceHelpers.IsWindowsService()) + { + _ = webApplicationBuilder.Services.AddSingleton(); + _ = webApplicationBuilder.Logging.AddEventLog(settings => + { +#pragma warning disable CA1416 + if (string.IsNullOrEmpty(settings.SourceName)) + settings.SourceName = webApplicationBuilder.Environment.ApplicationName; +#pragma warning restore + }); + } + WebApplication webApplication = webApplicationBuilder.Build(); + logger = webApplication.Services.GetRequiredService>(); + _ = webApplication.UseCors(corsPolicyBuilder => corsPolicyBuilder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()); + if (!webApplicationBuilder.Environment.IsDevelopment()) + { + _ = webApplication.UseExceptionHandler("/Error"); + _ = webApplication.UseHttpsRedirection(); + _ = webApplication.UseHsts(); + } + else + { + if (string.IsNullOrEmpty(appSettings.URLs)) + { + Environment.ExitCode = -1; + webApplication.Lifetime.StopApplication(); + } + _ = webApplication.UseSwagger(); + _ = webApplication.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Server V1")); + } + _ = webApplication.UseSession(); + _ = webApplication.MapControllers(); + logger.LogInformation("Starting Web Application"); + webApplication.Run(); + return 0; + } + catch (Exception ex) + { + try + { logger?.LogCritical(ex, "Host terminated unexpectedly"); } + catch (Exception) { } + throw; + } + } + +} \ No newline at end of file diff --git a/Wafer-Counter/Properties/launchSettings.json b/Wafer-Counter/Properties/launchSettings.json new file mode 100644 index 0000000..b3bc698 --- /dev/null +++ b/Wafer-Counter/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5126", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7130;http://localhost:5126", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https-prod": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7130;http://localhost:5126", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Production" + } + } + } +} \ No newline at end of file diff --git a/Wafer-Counter/Repositories/AppSettingsRepository.cs b/Wafer-Counter/Repositories/AppSettingsRepository.cs new file mode 100644 index 0000000..41fe830 --- /dev/null +++ b/Wafer-Counter/Repositories/AppSettingsRepository.cs @@ -0,0 +1,40 @@ +using OI.Metrology.Wafer.Counter.Models; +using OI.Metrology.Shared.Models.Stateless; + +namespace OI.Metrology.Wafer.Counter.Repository; + +public class AppSettingsRepository : IAppSettingsRepository +{ + + private readonly AppSettings _AppSettings; + + public AppSettingsRepository(AppSettings appSettings) => _AppSettings = appSettings; + + internal Models.Binder.AppSettings GetAppSettings() + { + Models.Binder.AppSettings result = new() + { + BuildNumber = _AppSettings.BuildNumber, + Company = _AppSettings.Company, + GitCommitSeven = _AppSettings.GitCommitSeven, + IsDevelopment = _AppSettings.IsDevelopment, + IsStaging = _AppSettings.IsStaging, + }; + return result; + } + + Models.Binder.AppSettings IAppSettingsRepository.GetAppSettings() => GetAppSettings(); + + internal string GetBuildNumberAndGitCommitSeven() + { + string result = string.Concat(_AppSettings.BuildNumber, '-', _AppSettings.GitCommitSeven); + return result; + } + + string IAppSettingsRepository.GetBuildNumberAndGitCommitSeven() => + GetBuildNumberAndGitCommitSeven(); + + void IAppSettingsRepository.VerifyConnectionStrings() => + throw new NotImplementedException(); + +} \ No newline at end of file diff --git a/Wafer-Counter/Repositories/FileShareRepository.cs b/Wafer-Counter/Repositories/FileShareRepository.cs new file mode 100644 index 0000000..1fc78ff --- /dev/null +++ b/Wafer-Counter/Repositories/FileShareRepository.cs @@ -0,0 +1,99 @@ +using OI.Metrology.Shared.Models; +using OI.Metrology.Shared.Models.Stateless; +using System.Text.Json; + +namespace OI.Metrology.Wafer.Counter.Repository; + +public class FileShareRepository : IFileShareRepository +{ + + Uri IFileShareRepository.Append(Uri uri, params string[] paths) => + new(paths.Aggregate(uri.AbsoluteUri, (current, path) => + string.Format("{0}/{1}", current.TrimEnd('/'), path.TrimStart('/')))); + + private Uri GetEndPoint(HttpClient httpClient, string method) + { + Uri result; + if (httpClient.BaseAddress is null) + throw new NullReferenceException(nameof(httpClient.BaseAddress)); + IFileShareRepository fileShareRepository = this; + result = fileShareRepository.Append(httpClient.BaseAddress, "api", "v1", "file-share", method); + return result; + } + + void IFileShareRepository.CopyFile(string from, string to) + { + string directory = Path.GetDirectoryName(to) ?? throw new NullReferenceException(); + if (!Directory.Exists(directory)) + _ = Directory.CreateDirectory(directory); + File.Copy(from, to); + } + + void IFileShareRepository.MoveFile(string from, string to) + { + string directory = Path.GetDirectoryName(to) ?? throw new NullReferenceException(); + if (!Directory.Exists(directory)) + _ = Directory.CreateDirectory(directory); + if (File.Exists(to)) + File.Move(to, $"{to}.{DateTime.Now.Ticks}.old"); + File.Move(from, to); + } + + void IFileShareRepository.FileWrite(string path, string contents) + { + string directory = Path.GetDirectoryName(path) ?? throw new NullReferenceException(); + if (!Directory.Exists(directory)) + _ = Directory.CreateDirectory(directory); + File.WriteAllText(path, contents); + } + + void IFileShareRepository.CopyFile(HttpClient httpClient, string from, string to) + { + Uri uri = GetEndPoint(httpClient, "copy-file"); + Task httpResponseMessage = httpClient.GetAsync(uri); + httpResponseMessage.Wait(); + if (httpResponseMessage.Result.StatusCode != System.Net.HttpStatusCode.OK) + throw new Exception(httpResponseMessage.Result.StatusCode.ToString()); + } + + void IFileShareRepository.MoveFile(HttpClient httpClient, string from, string to) + { + Uri uri = GetEndPoint(httpClient, "move-file"); + Task httpResponseMessage = httpClient.GetAsync(uri); + httpResponseMessage.Wait(); + if (httpResponseMessage.Result.StatusCode != System.Net.HttpStatusCode.OK) + throw new Exception(httpResponseMessage.Result.StatusCode.ToString()); + } + + HttpResponseMessage IFileShareRepository.ReadFile(HttpClient httpClient, Uri uri) + { + HttpResponseMessage result; + Task httpResponseMessage = httpClient.GetAsync(uri); + httpResponseMessage.Wait(); + result = httpResponseMessage.Result; + return result; + } + + List IFileShareRepository.GetNginxFileSystemSortableCollection(HttpClient httpClient, Uri uri, string? endsWith) + { + List results = new(); + Task httpResponseMessage = httpClient.GetAsync(uri); + httpResponseMessage.Wait(); + if (httpResponseMessage.Result.StatusCode == System.Net.HttpStatusCode.OK) + { + FileShareRepository fileShareRepository = this; + Task json = httpResponseMessage.Result.Content.ReadAsStringAsync(); + json.Wait(); + NginxFileSystem[]? nginxFileSystemCollection = JsonSerializer.Deserialize(json.Result, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray); + List nginxFileSystemSortableCollection = NginxFileSystemSortable.Convert(fileShareRepository, uri, nginxFileSystemCollection); + foreach (NginxFileSystemSortable nginxFileSystemSortable in nginxFileSystemSortableCollection.OrderByDescending(l => l.DateTime)) + { + if (!string.IsNullOrEmpty(endsWith) && !nginxFileSystemSortable.Name.EndsWith(endsWith)) + continue; + results.Add(nginxFileSystemSortable); + } + } + return results; + } + +} \ No newline at end of file diff --git a/Server/Repositories/WaferCounterRepository.cs b/Wafer-Counter/Repositories/WaferCounterRepository.cs similarity index 96% rename from Server/Repositories/WaferCounterRepository.cs rename to Wafer-Counter/Repositories/WaferCounterRepository.cs index 086ed72..d835fcf 100644 --- a/Server/Repositories/WaferCounterRepository.cs +++ b/Wafer-Counter/Repositories/WaferCounterRepository.cs @@ -1,11 +1,11 @@ -using OI.Metrology.Server.Models; +using OI.Metrology.Wafer.Counter.Models; using OI.Metrology.Shared.DataModels; using OI.Metrology.Shared.Models; using OI.Metrology.Shared.Models.Stateless; using OI.Metrology.Shared.Repositories; using System.Globalization; -namespace OI.Metrology.Server.Repository; +namespace OI.Metrology.Wafer.Counter.Repository; public class WaferCounterRepository : IWaferCounterRepository { @@ -20,7 +20,6 @@ public class WaferCounterRepository : IWaferCounterRepository private readonly IHttpClientFactory _HttpClientFactory; private readonly IDbConnectionFactory _DBConnectionFactory; private readonly IFileShareRepository _FileShareRepository; - private readonly string _OpenInsightApplicationProgrammingInterface; public WaferCounterRepository(AppSettings appSettings, IDbConnectionFactory dbConnectionFactory, IHttpClientFactory httpClientFactory, IFileShareRepository fileShareRepository) { @@ -30,7 +29,6 @@ public class WaferCounterRepository : IWaferCounterRepository _DBConnectionFactory = dbConnectionFactory; _FileShareRepository = fileShareRepository; _RepositoryName = nameof(WaferCounterRepository)[..^10]; - _OpenInsightApplicationProgrammingInterface = appSettings.OpenInsightApplicationProgrammingInterface; } private void MoveFile(string waferSizeDirectory, NginxFileSystemSortable nginxFileSystemSortable)