Compare commits
	
		
			3 Commits
		
	
	
		
			4c2bef71ec
			...
			red-hat
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 85fe0a7823 | |||
| 811f45a7df | |||
| 127634f5ab | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -344,5 +344,3 @@ ASALocalRun/ | ||||
|  | ||||
| .kanbn | ||||
| Tests/.kanbn | ||||
|  | ||||
| /Wafer-Counter/.vscode/.UserSecrets/secrets.json | ||||
							
								
								
									
										6
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -217,6 +217,12 @@ | ||||
|                     "endsPattern": "^.*Application started.*" | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             "label": "File-Folder-Helper AOT s M Self .Kanbn Tasks", | ||||
|             "type": "shell", | ||||
|             "command": "& L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net7.0/win-x64/publish/File-Folder-Helper.exe s M '.kanbn/tasks'", | ||||
|             "problemMatcher": [] | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										84
									
								
								Server/.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -13,44 +13,6 @@ | ||||
|             ], | ||||
|             "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", | ||||
| @ -66,16 +28,6 @@ | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Format-Whitespace", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "whitespace" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "publish", | ||||
|             "command": "dotnet", | ||||
| @ -84,32 +36,7 @@ | ||||
|                 "publish", | ||||
|                 "${workspaceFolder}/OI.Metrology.Server.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary", | ||||
|                 "--configuration", | ||||
|                 "Release", | ||||
|                 "--runtime", | ||||
|                 "win-x64", | ||||
|                 "--self-contained", | ||||
|                 "-o", | ||||
|                 "D:/web-sites/OI-Metrology/hh-3498d1da-_______-OI-Metrology-Release/Server" | ||||
|             ], | ||||
|             "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" | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
| @ -160,14 +87,9 @@ | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             "label": "File-Folder-Helper AOT s X Server", | ||||
|             "label": "File-Folder-Helper AOT s V Repositories", | ||||
|             "type": "shell", | ||||
|             "command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe", | ||||
|             "args": [ | ||||
|                 "s", | ||||
|                 "X", | ||||
|                 "L:/DevOps/Mesa_FI/OI-Metrology Day-Helper-2024-01-08 L:/DevOps/Mesa_FI/OI-Metrology/Server" | ||||
|             ], | ||||
|             "command": "& L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net7.0/win-x64/publish/File-Folder-Helper.exe s V Repositories", | ||||
|             "problemMatcher": [] | ||||
|         } | ||||
|     ] | ||||
|  | ||||
| @ -38,10 +38,4 @@ public class AwaitingDispoController : Controller, IAwaitingDispoController<IAct | ||||
|         else | ||||
|             return StatusCode(444); | ||||
|     } | ||||
|  | ||||
|     // this endpoint is used to clear the ReviewDate column, causing the header to show up again | ||||
|     [HttpGet("/api/awaitingdispo/{toolTypeId}/header-attachment-id")] | ||||
|     public IActionResult GetHeaderAttachmentID(int toolTypeId, [FromQuery] long headerid) => | ||||
|         Content(_MetrologyRepository.GetHeaderAttachmentID(toolTypeId, headerid).ToString()); | ||||
|  | ||||
| } | ||||
| @ -4,7 +4,6 @@ namespace OI.Metrology.Server.ApiControllers; | ||||
|  | ||||
| using OI.Metrology.Shared.DataModels; | ||||
| using OI.Metrology.Shared.Models.Stateless; | ||||
| using System.Data; | ||||
| using System.Text.Json; | ||||
|  | ||||
| [Route("api/[controller]")] | ||||
| @ -12,13 +11,9 @@ public class ExportController : Controller, IExportController<IActionResult> | ||||
| { | ||||
|  | ||||
|     private readonly IExportRepository _ExportRepository; | ||||
|     private readonly IMetrologyRepository _MetrologyRepository; | ||||
|  | ||||
|     public ExportController(IExportRepository exportRepository, IMetrologyRepository metrologyRepository) | ||||
|     { | ||||
|     public ExportController(IExportRepository exportRepository) => | ||||
|         _ExportRepository = exportRepository; | ||||
|         _MetrologyRepository = metrologyRepository; | ||||
|     } | ||||
|  | ||||
|     private static string? GetJson(Stream stream) | ||||
|     { | ||||
| @ -84,21 +79,4 @@ public class ExportController : Controller, IExportController<IActionResult> | ||||
|     public IActionResult PostProcessDataStandardFormat() => | ||||
|         Content(_ExportRepository.GetProcessDataStandardFormat(GetHeaderCommon(Request.Body))); | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("{toolTypeId}/export")] | ||||
|     public IActionResult GetExportData(int toolTypeId, [FromQuery] string? datebegin, [FromQuery] string? dateend) | ||||
|     { | ||||
|         Result<DataTable> r = _ExportRepository.GetExportData(_MetrologyRepository, toolTypeId, datebegin, dateend); | ||||
|         string json = Newtonsoft.Json.JsonConvert.SerializeObject(r); | ||||
|         return Content(json); | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("{toolTypeId}/csv")] | ||||
|     public IActionResult GetCSVExport(int toolTypeId, [FromQuery] string? datebegin, [FromQuery] string? dateend, [FromQuery] string? filename) | ||||
|     { | ||||
|         string r = _ExportRepository.GetCSVExport(_MetrologyRepository, toolTypeId, datebegin, dateend); | ||||
|         return Content(r); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -22,7 +22,7 @@ public class FileShareController : Controller, IFileShareController<IResult> | ||||
|     [HttpGet("move-file")] | ||||
|     public IResult MoveFile(string from, string to) | ||||
|     { | ||||
|         _FileShareRepository.MoveFile(from, to); | ||||
|         _FileShareRepository.CopyFile(from, to); | ||||
|         return Results.Ok(); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -4,7 +4,6 @@ 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; | ||||
|  | ||||
| @ -30,9 +29,22 @@ public partial class InboundController : ControllerBase, IInboundController<IAct | ||||
|         _MetrologyRepository = metrologyRepository; | ||||
|     } | ||||
|  | ||||
|     private static string? GetJson(Stream stream) | ||||
|     { | ||||
|         string? result; | ||||
|         if (!stream.CanRead) | ||||
|             result = null; | ||||
|         else | ||||
|         { | ||||
|             Task<string> task = new StreamReader(stream).ReadToEndAsync(); | ||||
|             result = task.Result; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     [HttpPost] | ||||
|     [Route("{tooltype}")] | ||||
|     public IActionResult Post(string tooltype, JsonElement? jsonElement) | ||||
|     public IActionResult Post(string tooltype) | ||||
|     { | ||||
|         IPAddress? remoteIP = HttpContext.Connection.RemoteIpAddress; | ||||
|         if (!_InboundRepository.IsIPAddressAllowed(_AppSettings.InboundApiAllowedIPList, remoteIP)) | ||||
| @ -42,7 +54,8 @@ public partial class InboundController : ControllerBase, IInboundController<IAct | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             DataResponse dataResponse = _InboundRepository.Data(_MetrologyRepository, _InboundDataService, tooltype, jsonElement); | ||||
|             string? json = GetJson(Request.Body); | ||||
|             DataResponse dataResponse = _InboundRepository.Data(_MetrologyRepository, _InboundDataService, tooltype, json); | ||||
|             if (dataResponse.Errors.Count == 0) | ||||
|                 return Ok(dataResponse); | ||||
|             else | ||||
| @ -52,7 +65,7 @@ public partial class InboundController : ControllerBase, IInboundController<IAct | ||||
|  | ||||
|     [HttpPost] | ||||
|     [Route("{tooltype}/attachment")] | ||||
|     public IActionResult AttachFile(string tooltype, Attachment? attachment) | ||||
|     public IActionResult AttachFile(string tooltype, [FromQuery] long headerid, [FromQuery] string datauniqueid = "") | ||||
|     { | ||||
|         IPAddress? remoteIP = HttpContext.Connection.RemoteIpAddress; | ||||
|         if (!_InboundRepository.IsIPAddressAllowed(_AppSettings.InboundApiAllowedIPList, remoteIP)) | ||||
| @ -62,8 +75,16 @@ public partial class InboundController : ControllerBase, IInboundController<IAct | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             _InboundRepository.AttachFile(_MetrologyRepository, _AttachmentsService, tooltype, attachment); | ||||
|             if (Request.Form is null) | ||||
|                 return BadRequest($"Invalid form"); | ||||
|             if (Request.Form.Files.Count != 1) | ||||
|                 return BadRequest($"Invalid file count"); | ||||
|             IFormFile formFile = Request.Form.Files[0]; | ||||
|             string? message = _InboundRepository.AttachFile(_MetrologyRepository, _AttachmentsService, tooltype, headerid, datauniqueid, formFile.FileName, formFile); | ||||
|             if (message is null) | ||||
|                 return Ok(); | ||||
|             else | ||||
|                 return BadRequest(message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -79,8 +79,12 @@ public class InfinityQSV4Controller : Controller, IInfinityQSV4Controller<IActio | ||||
|     public IActionResult GetProductionSpecification(string part) => | ||||
|        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)); | ||||
|     [HttpGet("engineering-spc-review")] | ||||
|     public IActionResult GetEngineeringSpcReview() | ||||
|     { | ||||
|         Dictionary<string, List<string>> results = _InfinityQSRepositoryV4.GetEngineeringSpcReview(); | ||||
|         string json = JsonSerializer.Serialize(results); | ||||
|         return Content(json, "application/json", System.Text.Encoding.UTF8); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -66,6 +66,23 @@ public class ToolTypesController : Controller, IToolTypesController<IActionResul | ||||
|         return Content(json); | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("{toolTypeId}/export")] | ||||
|     public IActionResult GetExportData(int toolTypeId, [FromQuery] string? datebegin, [FromQuery] string? dateend) | ||||
|     { | ||||
|         Shared.DataModels.Result<DataTable> r = _ToolTypesRepository.GetExportData(_MetrologyRepo, toolTypeId, datebegin, dateend); | ||||
|         string json = JsonConvert.SerializeObject(r); | ||||
|         return Content(json); | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("{toolTypeId}/csv")] | ||||
|     public IActionResult GetCSVExport(int toolTypeId, [FromQuery] string? datebegin, [FromQuery] string? dateend, [FromQuery] string? filename) | ||||
|     { | ||||
|         byte[] r = _ToolTypesRepository.GetCSVExport(_MetrologyRepo, toolTypeId, datebegin, dateend); | ||||
|         return File(r, "application/octet-stream", filename); | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("{toolTypeId}/{tabletype}/files/{attachmentId}/{filename}")] | ||||
|     public IActionResult GetAttachment(int toolTypeId, string tabletype, string attachmentId, string filename) | ||||
|  | ||||
							
								
								
									
										22
									
								
								Server/ApiControllers/WaferCounterController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,22 @@ | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using OI.Metrology.Shared.Models.Stateless; | ||||
|  | ||||
| namespace OI.Metrology.Server.ApiControllers; | ||||
|  | ||||
| [Route("api/v1/WaferCounter")] | ||||
| public class WaferCounterController : Controller, IWaferCounterController<IActionResult> | ||||
| { | ||||
|  | ||||
|     private readonly IWaferCounterRepository _WaferCounterRepository; | ||||
|  | ||||
|     public WaferCounterController(IWaferCounterRepository waferCounterRepository) => | ||||
|         _WaferCounterRepository = waferCounterRepository; | ||||
|  | ||||
|     [HttpGet("{waferSize}/last-quantity-and-slot-map")] | ||||
|     public IActionResult GetLastQuantityAndSlotMap(string area, string waferSize) => | ||||
|         Json(_WaferCounterRepository.GetLastQuantityAndSlotMap(area, waferSize)); | ||||
|  | ||||
|     [HttpGet("{waferSize}/last-quantity-and-slot-map-with-text")] | ||||
|     public IActionResult GetLastQuantityAndSlotMapWithText(string area, string waferSize, string text) => | ||||
|         Json(_WaferCounterRepository.GetLastQuantityAndSlotMapWithText(area, waferSize, text)); | ||||
| } | ||||
							
								
								
									
										160
									
								
								Server/Controllers/ExportController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,160 @@ | ||||
| using Infineon.Monitoring.MonA; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.AspNetCore.Mvc.Filters; | ||||
| using OI.Metrology.Server.Models; | ||||
| using OI.Metrology.Shared.DataModels; | ||||
| using OI.Metrology.Shared.Models.Stateless; | ||||
| using OI.Metrology.Shared.ViewModels; | ||||
| using System.Text; | ||||
|  | ||||
| namespace OI.Metrology.Server.Controllers; | ||||
|  | ||||
| public class ExportController : Controller | ||||
| { | ||||
|  | ||||
|     private readonly ILogger _Logger; | ||||
|     private readonly AppSettings _AppSettings; | ||||
|     private readonly bool _IsTestDatabase; | ||||
|     private readonly IMetrologyRepository _MetrologyRepository; | ||||
|  | ||||
|     public ExportController(AppSettings appSettings, ILogger<ExportController> logger, IMetrologyRepository metrologyRepository) | ||||
|     { | ||||
|         _Logger = logger; | ||||
|         _AppSettings = appSettings; | ||||
|         _MetrologyRepository = metrologyRepository; | ||||
|         _IsTestDatabase = appSettings.ConnectionString.Contains("test", StringComparison.InvariantCultureIgnoreCase); | ||||
|     } | ||||
|  | ||||
|     public override void OnActionExecuted(ActionExecutedContext context) | ||||
|     { | ||||
|         base.OnActionExecuted(context); | ||||
|         ViewBag.IsTestDatabase = _IsTestDatabase; | ||||
|     } | ||||
|  | ||||
|     private string GetApiUrl() => string.IsNullOrEmpty(_AppSettings.ApiUrl) ? Url.Content("~/") : _AppSettings.ApiUrl[0] == '~' ? Url.Content(_AppSettings.ApiUrl) : _AppSettings.ApiUrl; | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("/Export")] | ||||
|     public ActionResult Index() | ||||
|     { | ||||
|         Export model = new() | ||||
|         { | ||||
|             StartTime = DateTime.Now.AddMonths(-1), | ||||
|             EndTime = DateTime.Now | ||||
|         }; | ||||
|         MonIn monIn = MonIn.GetInstance(); | ||||
|         _ = monIn.SendStatus(_AppSettings.MonASite, _AppSettings.MonAResource, "Heartbeat", State.Up); | ||||
|         ViewBag.ApiUrl = GetApiUrl(); | ||||
|         return View(model); | ||||
|     } | ||||
|  | ||||
|     [HttpPost] | ||||
|     [Route("/ExportData")] | ||||
|     public ActionResult ExportData(Export model) | ||||
|     { | ||||
|         ToolType? toolType = null; | ||||
|         if (string.IsNullOrEmpty(model.ToolType)) | ||||
|             ModelState.AddModelError("Exception", "Invalid selection"); | ||||
|         else | ||||
|         { | ||||
|             if (model.StartTime > model.EndTime) | ||||
|                 ModelState.AddModelError("EndTime", "End time must be after start time"); | ||||
|             IEnumerable<ToolType> toolTypes = _MetrologyRepository.GetToolTypes(); | ||||
|             toolType = toolTypes.Where(tt => tt.ID.ToString() == model.ToolType).SingleOrDefault(); | ||||
|             if (toolType is null) | ||||
|                 ModelState.AddModelError("ToolType", "Invalid selection"); | ||||
|             else if (string.IsNullOrWhiteSpace(toolType.ExportSPName)) | ||||
|                 ModelState.AddModelError("ToolType", "Tool type is not exportable"); | ||||
|         } | ||||
|         if (ModelState.IsValid) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 DateTime startDT = model.StartDate.Date.Add(model.StartTime.TimeOfDay); | ||||
|                 DateTime endDT = model.EndDate.Date.Add(model.EndTime.TimeOfDay); | ||||
|  | ||||
|                 return DoCSVExport(toolType?.ToolTypeName, toolType?.ExportSPName, startDT, endDT); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 ModelState.AddModelError("Exception", "Error exporting data"); | ||||
|                 ModelState.AddModelError("Exception", ex.Message); | ||||
|                 string errorMessage = $"Error exporting: {ex}"; | ||||
|                 _Logger.LogError(message: errorMessage); | ||||
|                 MonIn monIn = MonIn.GetInstance(); | ||||
|                 _ = monIn.SendStatus(_AppSettings.MonASite, _AppSettings.MonAResource, "Heartbeat", State.Warning); | ||||
|             } | ||||
|         } | ||||
|         ViewBag.ApiUrl = GetApiUrl(); | ||||
|         return View("Index", model); | ||||
|     } | ||||
|  | ||||
|     protected ActionResult DoCSVExport(string? toolTypeName, string? spName, DateTime startTime, DateTime endTime) | ||||
|     { | ||||
|         string fileName = string.Format("Export_{0}_{1:yyyyMMddHHmm}_to_{2:yyyyMMddHHmm}.csv", toolTypeName, startTime, endTime); | ||||
|         StringBuilder sb = new(); | ||||
|         if (spName is null) | ||||
|             throw new NullReferenceException(nameof(spName)); | ||||
|         System.Data.DataTable dt = _MetrologyRepository.ExportData(spName, startTime, endTime); | ||||
|         _ = sb.AppendLine(GetColumnHeaders(dt)); | ||||
|         foreach (System.Data.DataRow dr in dt.Rows) | ||||
|             _ = sb.AppendLine(GetRowData(dr)); | ||||
|         byte[] contents = Encoding.UTF8.GetBytes(sb.ToString()); | ||||
|         return File(contents, "application/octet-stream", fileName); | ||||
|     } | ||||
|  | ||||
|     protected static string GetRowData(System.Data.DataRow dr) | ||||
|     { | ||||
|         StringBuilder r = new(); | ||||
|         for (int i = 0; i < dr.Table.Columns.Count; i++) | ||||
|         { | ||||
|             if (i > 0) | ||||
|                 _ = r.Append(','); | ||||
|             object v = dr[i]; | ||||
|             if (!Convert.IsDBNull(v)) | ||||
|             { | ||||
|                 string? c = Convert.ToString(v); | ||||
|                 if (c is not null) | ||||
|                     _ = r.Append(FormatForCSV(c)); | ||||
|             } | ||||
|         } | ||||
|         return r.ToString(); | ||||
|     } | ||||
|  | ||||
|     protected static string GetColumnHeaders(System.Data.DataTable dt) | ||||
|     { | ||||
|         StringBuilder r = new(); | ||||
|         for (int i = 0; i < dt.Columns.Count; i++) | ||||
|         { | ||||
|             if (i > 0) | ||||
|                 _ = r.Append(','); | ||||
|  | ||||
|             _ = r.Append(FormatForCSV(dt.Columns[i].ColumnName.TrimEnd('_'))); | ||||
|         } | ||||
|         return r.ToString(); | ||||
|     } | ||||
|  | ||||
|     protected static string FormatForCSV(string v) | ||||
|     { | ||||
|         bool doubleQuoted = false; | ||||
|         StringBuilder r = new(v.Length + 2); | ||||
|         if (v.StartsWith(' ') || v.EndsWith(' ') || v.Contains(',') || v.Contains('"')) | ||||
|         { | ||||
|             _ = r.Append('"'); | ||||
|             doubleQuoted = true; | ||||
|         } | ||||
|         foreach (char c in v) | ||||
|         { | ||||
|             _ = c switch | ||||
|             { | ||||
|                 '\r' or '\n' => r.Append(' '), | ||||
|                 '"' => r.Append("\"\""), | ||||
|                 _ => r.Append(c), | ||||
|             }; | ||||
|         } | ||||
|         if (doubleQuoted) | ||||
|             _ = r.Append('"'); | ||||
|         return r.ToString(); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										79
									
								
								Server/Controllers/PagesController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,79 @@ | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.AspNetCore.Mvc.Filters; | ||||
| using OI.Metrology.Server.Models; | ||||
| using OI.Metrology.Shared.Models.Stateless; | ||||
| using OI.Metrology.Shared.ViewModels; | ||||
|  | ||||
| namespace OI.Metrology.Server.Controllers; | ||||
|  | ||||
| public class PagesController : Controller | ||||
| { | ||||
|  | ||||
|     private readonly bool _IsTestDatabase; | ||||
|     private readonly AppSettings _AppSettings; | ||||
|     private readonly IMetrologyRepository _MetrologyRepository; | ||||
|  | ||||
|     public PagesController(AppSettings appSettings, IMetrologyRepository metrologyRepository) | ||||
|     { | ||||
|         _AppSettings = appSettings; | ||||
|         _MetrologyRepository = metrologyRepository; | ||||
|         _IsTestDatabase = appSettings.ConnectionString.Contains("test", StringComparison.InvariantCultureIgnoreCase); | ||||
|     } | ||||
|  | ||||
|     public override void OnActionExecuted(ActionExecutedContext context) | ||||
|     { | ||||
|         base.OnActionExecuted(context); | ||||
|         ViewBag.IsTestDatabase = _IsTestDatabase; | ||||
|     } | ||||
|  | ||||
|     private string GetApiUrl() => string.IsNullOrEmpty(_AppSettings.ApiUrl) ? Url.Content("~/") : _AppSettings.ApiUrl[0] == '~' ? Url.Content(_AppSettings.ApiUrl) : _AppSettings.ApiUrl; | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("/")] | ||||
|     public IActionResult Index() | ||||
|     { | ||||
|         ViewBag.ApiUrl = GetApiUrl(); | ||||
|         return View("AwaitingDispo"); | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("/AwaitingDispo")] | ||||
|     [Route("/Metrology/AwaitingDispo")] | ||||
|     public IActionResult AwaitingDispo() | ||||
|     { | ||||
|         ViewBag.ApiUrl = GetApiUrl(); | ||||
|         return View(); | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("/RunInfo")] | ||||
|     [Route("/Metrology/RunInfo")] | ||||
|     public IActionResult RunInfo([FromQuery] int tooltypeid = 1, [FromQuery] int headerid = 0) | ||||
|     { | ||||
|         RunInfo m = new() | ||||
|         { | ||||
|             ToolTypeID = tooltypeid, | ||||
|             HeaderID = headerid, | ||||
|             HeaderAttachmentID = Guid.Empty, | ||||
|         }; | ||||
|         if (headerid > 0) | ||||
|         { | ||||
|             m.HeaderAttachmentID = _MetrologyRepository.GetHeaderAttachmentID(tooltypeid, headerid); | ||||
|         } | ||||
|         ViewBag.ApiUrl = GetApiUrl(); | ||||
|         return View(m); | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("/RunHeaders")] | ||||
|     [Route("/Metrology/RunHeaders")] | ||||
|     public IActionResult RunHeaders() | ||||
|     { | ||||
|         ViewBag.ApiUrl = GetApiUrl(); | ||||
|         return View(); | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("/Crash")] | ||||
|     public IActionResult Crash() => throw new Exception("Test unhandled exception"); | ||||
| } | ||||
| @ -13,7 +13,6 @@ public record AppSettings(string ApiFileShare, | ||||
|                           string EcCharacterizationSi, | ||||
|                           string EcMesaFileShareCharacterizationSi, | ||||
|                           string EcMesaFileShareMetrologySi, | ||||
|                           string EcMetrologySi, | ||||
|                           string GitCommitSeven, | ||||
|                           string InboundApiAllowedIPList, | ||||
|                           string IqsColumns, | ||||
|  | ||||
| @ -17,7 +17,6 @@ public class AppSettings | ||||
|     public string? EcCharacterizationSi { get; set; } | ||||
|     public string? EcMesaFileShareCharacterizationSi { get; set; } | ||||
|     public string? EcMesaFileShareMetrologySi { get; set; } | ||||
|     public string? EcMetrologySi { get; set; } | ||||
|     public string? GitCommitSeven { get; set; } | ||||
|     public string? InboundApiAllowedIPList { get; set; } | ||||
|     public string? IqsColumns { get; set; } | ||||
| @ -75,7 +74,6 @@ public class AppSettings | ||||
|         if (appSettings.EcCharacterizationSi is null) throw new NullReferenceException(nameof(EcCharacterizationSi)); | ||||
|         if (appSettings.EcMesaFileShareCharacterizationSi is null) throw new NullReferenceException(nameof(EcMesaFileShareCharacterizationSi)); | ||||
|         if (appSettings.EcMesaFileShareMetrologySi is null) throw new NullReferenceException(nameof(EcMesaFileShareMetrologySi)); | ||||
|         if (appSettings.EcMetrologySi is null) throw new NullReferenceException(nameof(EcMetrologySi)); | ||||
|         if (appSettings.GitCommitSeven is null) throw new NullReferenceException(nameof(GitCommitSeven)); | ||||
|         if (appSettings.InboundApiAllowedIPList is null) throw new NullReferenceException(nameof(InboundApiAllowedIPList)); | ||||
|         if (appSettings.IqsColumns is null) throw new NullReferenceException(nameof(IqsColumns)); | ||||
| @ -106,7 +104,6 @@ public class AppSettings | ||||
|             appSettings.EcCharacterizationSi, | ||||
|             appSettings.EcMesaFileShareCharacterizationSi, | ||||
|             appSettings.EcMesaFileShareMetrologySi, | ||||
|             appSettings.EcMetrologySi, | ||||
|             appSettings.GitCommitSeven, | ||||
|             appSettings.InboundApiAllowedIPList, | ||||
|             appSettings.IqsColumns, | ||||
|  | ||||
| @ -6,7 +6,6 @@ | ||||
|         <SccLocalPath>SAK</SccLocalPath> | ||||
|     </PropertyGroup> | ||||
|     <PropertyGroup> | ||||
|         <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild> | ||||
|         <ImplicitUsings>enable</ImplicitUsings> | ||||
|         <IsPackable>false</IsPackable> | ||||
|         <Nullable>enable</Nullable> | ||||
| @ -25,10 +24,10 @@ | ||||
|         <Content Remove="compilerconfig.json" /> | ||||
|     </ItemGroup> | ||||
|     <ItemGroup> | ||||
|         <PackageReference Include="Dapper" Version="2.1.44" /> | ||||
|         <PackageReference Include="EntityFramework" Version="6.5.1" /> | ||||
|         <PackageReference Include="Dapper" Version="2.1.24" /> | ||||
|         <PackageReference Include="EntityFramework" Version="6.4.4" /> | ||||
|         <PackageReference Include="jQuery" Version="3.7.1" /> | ||||
|         <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.7" /> | ||||
|         <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.0" /> | ||||
|         <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" /> | ||||
|         <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" /> | ||||
|         <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" /> | ||||
| @ -38,9 +37,9 @@ | ||||
|         <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" /> | ||||
|         <PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="8.0.0" /> | ||||
|         <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> | ||||
|         <PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" /> | ||||
|         <PackageReference Include="System.Data.SqlClient" Version="4.8.6" /> | ||||
|         <PackageReference Include="System.Drawing.Common" Version="8.0.7" /> | ||||
|         <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> | ||||
|         <PackageReference Include="System.Data.SqlClient" Version="4.8.5" /> | ||||
|         <PackageReference Include="System.Drawing.Common" Version="8.0.0" /> | ||||
|     </ItemGroup> | ||||
|     <ItemGroup> | ||||
|         <ProjectReference Include="..\Shared\OI.Metrology.Shared.csproj" /> | ||||
|  | ||||
| @ -74,6 +74,7 @@ public class Program | ||||
|             _ = webApplicationBuilder.Services.AddSingleton<IInfinityQSV2Repository, InfinityQSV2Repository>(); | ||||
|             _ = webApplicationBuilder.Services.AddSingleton<IInfinityQSV3Repository, InfinityQSV3Repository>(); | ||||
|             _ = webApplicationBuilder.Services.AddSingleton<IInfinityQSV4Repository, InfinityQSV4Repository>(); | ||||
|             _ = webApplicationBuilder.Services.AddSingleton<IWaferCounterRepository, WaferCounterRepository>(); | ||||
|             _ = webApplicationBuilder.Services.AddSingleton<IClientSettingsRepository>(_ => clientSettingsRepository); | ||||
|             _ = webApplicationBuilder.Services.AddSingleton<IServiceShopOrderRepository, ServiceShopOrderRepository>(); | ||||
|             _ = webApplicationBuilder.Services.AddSingleton<ISpreadingResistanceProfileService, SpreadingResistanceProfileService>(); | ||||
|  | ||||
| @ -2,9 +2,7 @@ using OI.Metrology.Server.Models; | ||||
| using OI.Metrology.Shared.DataModels; | ||||
| using OI.Metrology.Shared.Models; | ||||
| using OI.Metrology.Shared.Models.Stateless; | ||||
| using System.Data; | ||||
| using System.Globalization; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
|  | ||||
| namespace OI.Metrology.Server.Repository; | ||||
| @ -162,86 +160,4 @@ public class ExportRepository : IExportRepository | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     Result<DataTable> IExportRepository.GetExportData(IMetrologyRepository metrologyRepository, int toolTypeId, string? datebegin, string? dateend) | ||||
|     { | ||||
|         Result<DataTable>? r; | ||||
|         DateTime dateEnd = dateend is null ? DateTime.Now : DateTime.Parse(dateend); | ||||
|         DateTime dateBegin = datebegin is null ? dateEnd.AddMonths(-1) : DateTime.Parse(datebegin); | ||||
|         ToolType tt = metrologyRepository.GetToolTypeByID(toolTypeId); | ||||
|         if (string.IsNullOrEmpty(tt.ExportSPName)) | ||||
|             throw new NullReferenceException(nameof(tt.ExportSPName)); | ||||
|         DataTable dataTable = metrologyRepository.ExportData(tt.ExportSPName, dateBegin, dateEnd); | ||||
|         r = new() | ||||
|         { | ||||
|             Results = dataTable, | ||||
|             TotalRows = dataTable.Rows.Count, | ||||
|         }; | ||||
|         return r; | ||||
|     } | ||||
|  | ||||
|     protected static string FormatForCSV(string v) | ||||
|     { | ||||
|         StringBuilder result = new(v.Length + 2); | ||||
|         bool doubleQuoted = false; | ||||
|         if (v.StartsWith(' ') || v.EndsWith(' ') || v.Contains(',') || v.Contains('"')) | ||||
|         { | ||||
|             _ = result.Append('"'); | ||||
|             doubleQuoted = true; | ||||
|         } | ||||
|         foreach (char c in v) | ||||
|         { | ||||
|             _ = c switch | ||||
|             { | ||||
|                 '\r' or '\n' => result.Append(' '), | ||||
|                 '"' => result.Append("\"\""), | ||||
|                 _ => result.Append(c), | ||||
|             }; | ||||
|         } | ||||
|         if (doubleQuoted) | ||||
|             _ = result.Append('"'); | ||||
|         return result.ToString(); | ||||
|     } | ||||
|  | ||||
|     protected static string GetColumnHeaders(DataTable dataTable) | ||||
|     { | ||||
|         StringBuilder result = new(); | ||||
|         for (int i = 0; i < dataTable.Columns.Count; i++) | ||||
|         { | ||||
|             if (i > 0) | ||||
|                 _ = result.Append(','); | ||||
|             _ = result.Append(FormatForCSV(dataTable.Columns[i].ColumnName.TrimEnd('_'))); | ||||
|         } | ||||
|         return result.ToString(); | ||||
|     } | ||||
|  | ||||
|     protected static string GetRowData(DataRow dr) | ||||
|     { | ||||
|         StringBuilder result = new(); | ||||
|         for (int i = 0; i < dr.Table.Columns.Count; i++) | ||||
|         { | ||||
|             if (i > 0) | ||||
|                 _ = result.Append(','); | ||||
|             object v = dr[i]; | ||||
|             if (v is not null && !Convert.IsDBNull(v)) | ||||
|                 _ = result.Append(FormatForCSV(string.Concat(Convert.ToString(v)))); | ||||
|         } | ||||
|         return result.ToString(); | ||||
|     } | ||||
|  | ||||
|     string IExportRepository.GetCSVExport(IMetrologyRepository metrologyRepository, int toolTypeId, string? datebegin, string? dateend) | ||||
|     { | ||||
|         string results; | ||||
|         Result<DataTable> result; | ||||
|         IExportRepository repository = this; | ||||
|         result = repository.GetExportData(metrologyRepository, toolTypeId, datebegin, dateend); | ||||
|         if (result.Results is null) | ||||
|             throw new NullReferenceException(nameof(result.Results)); | ||||
|         StringBuilder stringBuilder = new(); | ||||
|         _ = stringBuilder.AppendLine(GetColumnHeaders(result.Results)); | ||||
|         foreach (DataRow dr in result.Results.Rows) | ||||
|             _ = stringBuilder.AppendLine(GetRowData(dr)); | ||||
|         results = stringBuilder.ToString(); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,7 +1,6 @@ | ||||
| using OI.Metrology.Shared.Models; | ||||
| using OI.Metrology.Shared.Models.Stateless; | ||||
| using System.Text.Json; | ||||
| using System.Web; | ||||
|  | ||||
| namespace OI.Metrology.Server.Repository; | ||||
|  | ||||
| @ -34,8 +33,6 @@ public class FileShareRepository : IFileShareRepository | ||||
|         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); | ||||
|     } | ||||
|  | ||||
| @ -50,9 +47,7 @@ public class FileShareRepository : IFileShareRepository | ||||
|     void IFileShareRepository.CopyFile(HttpClient httpClient, string from, string to) | ||||
|     { | ||||
|         Uri uri = GetEndPoint(httpClient, "copy-file"); | ||||
|         string encodedTo = HttpUtility.UrlEncode(to); | ||||
|         string encodedFrom = HttpUtility.UrlEncode(from); | ||||
|         Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync($"{uri.OriginalString}?from={encodedFrom}&to={encodedTo}"); | ||||
|         Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(uri); | ||||
|         httpResponseMessage.Wait(); | ||||
|         if (httpResponseMessage.Result.StatusCode != System.Net.HttpStatusCode.OK) | ||||
|             throw new Exception(httpResponseMessage.Result.StatusCode.ToString()); | ||||
| @ -61,9 +56,16 @@ public class FileShareRepository : IFileShareRepository | ||||
|     void IFileShareRepository.MoveFile(HttpClient httpClient, string from, string to) | ||||
|     { | ||||
|         Uri uri = GetEndPoint(httpClient, "move-file"); | ||||
|         string encodedTo = HttpUtility.UrlEncode(to); | ||||
|         string encodedFrom = HttpUtility.UrlEncode(from); | ||||
|         Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync($"{uri.OriginalString}?from={encodedFrom}&to={encodedTo}"); | ||||
|         Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(uri); | ||||
|         httpResponseMessage.Wait(); | ||||
|         if (httpResponseMessage.Result.StatusCode != System.Net.HttpStatusCode.OK) | ||||
|             throw new Exception(httpResponseMessage.Result.StatusCode.ToString()); | ||||
|     } | ||||
|  | ||||
|     void IFileShareRepository.FileWrite(HttpClient httpClient, string path, string contents) | ||||
|     { | ||||
|         Uri uri = GetEndPoint(httpClient, "file-write"); | ||||
|         Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(uri); | ||||
|         httpResponseMessage.Wait(); | ||||
|         if (httpResponseMessage.Result.StatusCode != System.Net.HttpStatusCode.OK) | ||||
|             throw new Exception(httpResponseMessage.Result.StatusCode.ToString()); | ||||
| @ -100,7 +102,4 @@ public class FileShareRepository : IFileShareRepository | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     List<CharacterizationInfo> IFileShareRepository.GetArchiveData(CharacterizationParameters archiveParameters) => | ||||
|         throw new NotImplementedException(); | ||||
|  | ||||
| } | ||||
| @ -31,17 +31,39 @@ public class InboundRepository : IInboundRepository | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     DataResponse IInboundRepository.Data(IMetrologyRepository metrologyRepository, IInboundDataService inboundDataService, string toolTypeName, JsonElement? jsonElement) | ||||
|     // 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 result = new(); | ||||
|         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}"); | ||||
|         ToolType? toolType = metrologyRepository.GetToolTypeByName(tooltype); | ||||
|         if (toolType is null) | ||||
|             result.Errors.Add($"Invalid tool type: {tooltype}"); | ||||
|         else | ||||
|         { | ||||
|             InboundCommon? inboundCommon = string.IsNullOrEmpty(json) ? null : JsonSerializer.Deserialize<InboundCommon>(json); | ||||
|             if (inboundCommon is null || string.IsNullOrEmpty(inboundCommon.ProcessDataStandardFormat)) | ||||
|                 result.Errors.Add($"Invalid body: {json}"); | ||||
|             else | ||||
|             { | ||||
|                 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<ToolTypeMetadata> 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) | ||||
|                             { | ||||
| @ -49,24 +71,36 @@ public class InboundRepository : IInboundRepository | ||||
|                                 { | ||||
|                                     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); } | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     void IInboundRepository.AttachFile(IMetrologyRepository metrologyRepository, IAttachmentsService attachmentsService, string toolTypeName, Attachment? attachment) | ||||
|     // 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) | ||||
|     { | ||||
|         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); | ||||
|         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; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -6,6 +6,7 @@ using OI.Metrology.Shared.Repositories; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Data; | ||||
| using System.Data.Common; | ||||
| using System.Reflection; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
|  | ||||
| @ -37,139 +38,124 @@ public class InfinityQSV4Repository : IInfinityQSV4Repository | ||||
|  | ||||
|     string IInfinityQSV4Repository.GetCommandText(string subGroupId) | ||||
|     { // cSpell:disable | ||||
|         List<string> results = []; | ||||
|         StringBuilder result = new(); | ||||
|         if (string.IsNullOrEmpty(subGroupId)) | ||||
|             throw new ArgumentException(null, nameof(subGroupId)); | ||||
|         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); | ||||
|         _ = 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(); | ||||
|     } // cSpell:enable | ||||
|  | ||||
|     string IInfinityQSV4Repository.GetCommandText(string? subGroupId, string? process, string? job, string? part, string? lot, string? dateTime) | ||||
|     { // cSpell:disable | ||||
|         List<string> results = []; | ||||
|         StringBuilder result = new(); | ||||
|         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)); | ||||
|         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) "); | ||||
|         _ = 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) "); | ||||
|         if (!string.IsNullOrEmpty(subGroupId)) | ||||
|             results.Add($"   and se.f_sgrp = {subGroupId.Split(" ")[0]} "); | ||||
|             _ = result.Append("   and se.f_sgrp = ").Append(subGroupId.Split(" ")[0]).AppendLine("  "); | ||||
|         if (!string.IsNullOrEmpty(process)) | ||||
|             results.Add($"   and pr.f_name = '{process}' "); | ||||
|             _ = result.Append("   and pr.f_name = '").Append(process).AppendLine("' "); | ||||
|         if (!string.IsNullOrEmpty(part)) | ||||
|             results.Add($"   and pd.f_name = '{part}' "); | ||||
|             _ = result.Append("   and pd.f_name = '").Append(part).AppendLine("' "); | ||||
|         if (!string.IsNullOrEmpty(job)) | ||||
|             results.Add($"   and jd.f_name = '{job}' "); | ||||
|             _ = result.Append("   and jd.f_name = '").Append(job).AppendLine("' "); | ||||
|         if (!string.IsNullOrEmpty(lot)) | ||||
|             results.Add($"   and pl.f_name = '{lot}' "); | ||||
|             _ = result.Append("   and pl.f_name = '").Append(lot).AppendLine("' "); | ||||
|         if (!string.IsNullOrEmpty(dateTime) && (dateTime.Contains('-') || dateTime.Contains(' ') || dateTime.Contains(':'))) | ||||
|             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); | ||||
|             _ = 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(); | ||||
|     } // cSpell:enable | ||||
|  | ||||
|     private JsonElement[] GetAllReactorsAsJsonElementElement() | ||||
|     { | ||||
|         JsonElement[]? results; | ||||
|         HttpClient httpClient = _HttpClientFactory.CreateClient(); | ||||
|         Task<string> task = httpClient.GetStringAsync($"{_OpenInsightApplicationProgrammingInterface}/reactors"); | ||||
|         task.Wait(); | ||||
|         JsonElement? jsonElement = JsonSerializer.Deserialize<JsonElement>(task.Result); | ||||
|         if (jsonElement is null) | ||||
|             throw new NullReferenceException(nameof(jsonElement)); | ||||
|         string json = jsonElement.Value.EnumerateObject().First().Value.ToString(); | ||||
|         results = JsonSerializer.Deserialize<JsonElement[]>(json); | ||||
|         if (results is null) | ||||
|             throw new NullReferenceException(nameof(results)); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private static StringBuilder GetForJsonPath(IDbConnectionFactory dbConnectionFactory, string commandText, bool useIqsConnection) | ||||
|     { | ||||
|         StringBuilder stringBuilder = new(); | ||||
|         File.WriteAllText("../../.sql", commandText); | ||||
|         using DbConnection dbConnection = dbConnectionFactory.GetDbConnection(useIqsConnection); | ||||
|         DbCommand dbCommand = dbConnection.CreateCommand(); | ||||
|         dbCommand.CommandText = commandText; | ||||
| @ -179,50 +165,6 @@ public class InfinityQSV4Repository : IInfinityQSV4Repository | ||||
|         return stringBuilder; | ||||
|     } | ||||
|  | ||||
|     private void GetOI(InfinityQSV4 infinityQSV4) | ||||
|     { | ||||
|         IInfinityQSV4Repository infinityQSV4Repository = this; | ||||
|         if (string.IsNullOrEmpty(infinityQSV4.Part)) | ||||
|             throw new ArgumentException(nameof(infinityQSV4.Part)); | ||||
|         string json = infinityQSV4Repository.GetProductionSpecification(infinityQSV4.Part); | ||||
|         File.WriteAllText("../../.json", json); | ||||
|         ProdSpecRoot? prodSpec = JsonSerializer.Deserialize(json, ProdSpecRootSourceGenerationContext.Default.ProdSpecRoot); | ||||
|         if (prodSpec is null) | ||||
|         { | ||||
|             if (prodSpec is null) | ||||
|             { } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static List<string> Convert(int[] night) | ||||
|     { | ||||
|         List<string> results = new(); | ||||
|         foreach (int reactor in night) | ||||
|             results.Add(reactor.ToString()); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private ReadOnlyDictionary<int, Reactor> GetReactorsMatchingType(string? reactTypeFilter) | ||||
|     { | ||||
|         Dictionary<int, Reactor> results = new(); | ||||
|         string json; | ||||
|         Reactor? reactor; | ||||
|         JsonElement[]? jsonElements = GetAllReactorsAsJsonElementElement(); | ||||
|         foreach (JsonElement jsonElement in jsonElements) | ||||
|         { | ||||
|             json = jsonElement.EnumerateObject().First().Value.ToString(); | ||||
|             if (reactTypeFilter is not null && !json.Contains(reactTypeFilter)) | ||||
|                 continue; | ||||
|             try | ||||
|             { reactor = JsonSerializer.Deserialize(json, ReactorSourceGenerationContext.Default.Reactor); } | ||||
|             catch (Exception) { reactor = null; } | ||||
|             if (reactor is null || (reactTypeFilter is not null && reactor.ReactType != reactTypeFilter)) | ||||
|                 continue; | ||||
|             results.Add(reactor.ReactorNo, reactor); | ||||
|         } | ||||
|         return new(results); | ||||
|     } | ||||
|  | ||||
|     private static InfinityQSV4 GetInfinityQSV4(IDbConnectionFactory dbConnectionFactory, IInfinityQSV4Repository infinityQSV4Repository, string subGroupId) | ||||
|     { | ||||
|         InfinityQSV4 result; | ||||
| @ -293,32 +235,33 @@ public class InfinityQSV4Repository : IInfinityQSV4Repository | ||||
|  | ||||
|     string IInfinityQSV4Repository.GetCommandText(InfinityQSV4 infinityQSV4) | ||||
|     { // cSpell:disable | ||||
|         List<string> results = []; | ||||
|         StringBuilder result = new(); | ||||
|         if (string.IsNullOrEmpty(infinityQSV4.Process)) | ||||
|             throw new ArgumentException(nameof(infinityQSV4.Process)); | ||||
|         if (string.IsNullOrEmpty(infinityQSV4.Part)) | ||||
|             throw new ArgumentException(nameof(infinityQSV4.Part)); | ||||
|         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); | ||||
|         _ = 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(); | ||||
|     } // cSpell:enable | ||||
|  | ||||
|     Result<InfinityQSV4[]> IInfinityQSV4Repository.GetHeader(string subGroupId) | ||||
| @ -347,27 +290,28 @@ public class InfinityQSV4Repository : IInfinityQSV4Repository | ||||
|  | ||||
|     string IInfinityQSV4Repository.GetCommandText(string process, string? part) | ||||
|     { // cSpell:disable | ||||
|         List<string> results = []; | ||||
|         StringBuilder result = new(); | ||||
|         if (string.IsNullOrEmpty(process)) | ||||
|             throw new ArgumentException(null, nameof(process)); | ||||
|         if (string.IsNullOrEmpty(part)) | ||||
|             throw new ArgumentException(null, nameof(part)); | ||||
|         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); | ||||
|         _ = 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(); | ||||
|     } // cSpell:enable | ||||
|  | ||||
|     string IInfinityQSV4Repository.GetProductDataAverageSumOfDefectsProcessMeanProcessSigma(string process, string? recipe) | ||||
| @ -389,56 +333,102 @@ public class InfinityQSV4Repository : IInfinityQSV4Repository | ||||
|         return result.ToString(); | ||||
|     } | ||||
|  | ||||
|     private JsonElement[] GetAllReactorsAsJsonElementElement() | ||||
|     { | ||||
|         JsonElement[]? results; | ||||
|         HttpClient httpClient = _HttpClientFactory.CreateClient(); | ||||
|         Task<string> task = httpClient.GetStringAsync($"{_OpenInsightApplicationProgrammingInterface}/reactors"); | ||||
|         task.Wait(); | ||||
|         JsonElement? jsonElement = JsonSerializer.Deserialize<JsonElement>(task.Result); | ||||
|         if (jsonElement is null) | ||||
|             throw new NullReferenceException(nameof(jsonElement)); | ||||
|         string json = jsonElement.Value.EnumerateObject().First().Value.ToString(); | ||||
|         results = JsonSerializer.Deserialize<JsonElement[]>(json); | ||||
|         if (results is null) | ||||
|             throw new NullReferenceException(nameof(results)); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private ReadOnlyDictionary<int, Reactor> GetReactorsMatchingType(string? reactTypeFilter) | ||||
|     { | ||||
|         Dictionary<int, Reactor> results = new(); | ||||
|         string json; | ||||
|         Reactor? reactor; | ||||
|         JsonElement[]? jsonElements = GetAllReactorsAsJsonElementElement(); | ||||
|         foreach (JsonElement jsonElement in jsonElements) | ||||
|         { | ||||
|             json = jsonElement.EnumerateObject().First().Value.ToString(); | ||||
|             if (reactTypeFilter is not null && !json.Contains(reactTypeFilter)) | ||||
|                 continue; | ||||
|             try | ||||
|             { reactor = JsonSerializer.Deserialize(json, ReactorSourceGenerationContext.Default.Reactor); } | ||||
|             catch (Exception) { reactor = null; } | ||||
|             if (reactor is null || (reactTypeFilter is not null && reactor.ReactType != reactTypeFilter)) | ||||
|                 continue; | ||||
|             results.Add(reactor.ReactorNo, reactor); | ||||
|         } | ||||
|         return new(results); | ||||
|     } | ||||
|  | ||||
|     string IInfinityQSV4Repository.GetCommandText(List<string> eppReactorNumbers) | ||||
|     { // cSpell:disable | ||||
|         List<string> 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); | ||||
|         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(); | ||||
|     } // cSpell:enable | ||||
|  | ||||
|     private static List<string> Convert(int[] night) | ||||
|     { | ||||
|         List<string> results = new(); | ||||
|         foreach (int reactor in night) | ||||
|             results.Add(reactor.ToString()); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     List<string[]> IInfinityQSV4Repository.GetEpiProTempVerificationRows(int[] night) | ||||
|     { | ||||
|         List<string[]>? results; | ||||
| @ -648,55 +638,171 @@ public class InfinityQSV4Repository : IInfinityQSV4Repository | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static string GetCommandText(string process, string? part, int? test) | ||||
|     { // cSpell:disable | ||||
|         List<string> 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) | ||||
|     private void GetOI(InfinityQSV4 infinityQSV4) | ||||
|     { | ||||
|         StringBuilder result; | ||||
|         IInfinityQSV4Repository infinityQSV4Repository = this; | ||||
|         if (string.IsNullOrEmpty(infinityQSV4.Part)) | ||||
|             throw new ArgumentException(nameof(infinityQSV4.Part)); | ||||
|         string json = infinityQSV4Repository.GetProductionSpecification(infinityQSV4.Part); | ||||
|         ProdSpecRoot? prodSpec = JsonSerializer.Deserialize(json, ProdSpecRootSourceGenerationContext.Default.ProdSpecRoot); | ||||
|         if (prodSpec is null) | ||||
|         { | ||||
|             if (prodSpec is null) | ||||
|             { } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static Record? GetValue(AppSettings appSettings, string line) | ||||
|     { | ||||
|         Record? result; | ||||
|         string[] attributes = line.Split('>'); | ||||
|         if (attributes.Length != 3) | ||||
|             result = null; | ||||
|         else | ||||
|         { | ||||
|             string[] text = attributes[1].Replace(appSettings.IqsRed, "red").Replace(appSettings.IqsYellow, "Yellow").Split($"</{line[1]}{line[2]}"); | ||||
|             if (text.Length != 2) | ||||
|                 result = null; | ||||
|             else | ||||
|             { | ||||
|                 string[] attributeValues = attributes[0].Split('"'); | ||||
|                 if (attributeValues.Length != 3) | ||||
|                     result = new(null, text[0]); | ||||
|                 else | ||||
|                 { | ||||
|                     result = new(attributeValues[1], text[0]); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyCollection<Record> GetRecords(AppSettings appSettings, string[] lines) | ||||
|     { | ||||
|         Record? record; | ||||
|         List<Record> results = new(); | ||||
|         List<string> checkColumns = new(); | ||||
|         foreach (string line in lines) | ||||
|         { | ||||
|             if (line.StartsWith("<th")) | ||||
|             { | ||||
|                 record = GetValue(appSettings, line); | ||||
|                 if (record is null) | ||||
|                     continue; | ||||
|                 checkColumns.Add(record.Text); | ||||
|             } | ||||
|             if (line.StartsWith("<td")) | ||||
|             { | ||||
|                 record = GetValue(appSettings, line); | ||||
|                 if (record is null) | ||||
|                     continue; | ||||
|                 results.Add(record); | ||||
|             } | ||||
|         } | ||||
|         if (string.Join(',', checkColumns) != appSettings.IqsColumns) | ||||
|             throw new NotSupportedException("Columns don't match!"); | ||||
|         return new(results); | ||||
|     } | ||||
|  | ||||
|     private static List<Dictionary<string, Record>> GetCollection(string columns, ReadOnlyCollection<Record> records) | ||||
|     { | ||||
|         string[] columnNames = columns.Split(','); | ||||
|         List<Dictionary<string, Record>> collection = new(); | ||||
|         Dictionary<string, Record> keyValuePairs; | ||||
|         for (int i = 0; i < records.Count; i++) | ||||
|         { | ||||
|             keyValuePairs = new(); | ||||
|             for (int j = 0; j < columnNames.Length; j++) | ||||
|             { | ||||
|                 keyValuePairs.Add(columnNames[j], records[i]); | ||||
|                 i++; | ||||
|             } | ||||
|             i--; | ||||
|             collection.Add(keyValuePairs); | ||||
|         } | ||||
|         return collection; | ||||
|     } | ||||
|  | ||||
|     private static Dictionary<string, List<string>> GetResults(AppSettings appSettings, List<Dictionary<string, Record>> collection) | ||||
|     { | ||||
|         Dictionary<string, List<string>> results = new(); | ||||
|         Record record; | ||||
|         List<string>? colors; | ||||
|         foreach (Dictionary<string, Record> keyValuePairs in collection) | ||||
|         { | ||||
|             record = keyValuePairs[appSettings.IqsKey]; | ||||
|             if (!results.TryGetValue(record.Text, out colors)) | ||||
|             { | ||||
|                 results.Add(record.Text, new()); | ||||
|                 if (!results.TryGetValue(record.Text, out colors)) | ||||
|                     throw new Exception(); | ||||
|             } | ||||
|             if (record.Color is null) | ||||
|                 continue; | ||||
|             colors.Add(record.Color.Replace(appSettings.IqsRed, "red").Replace(appSettings.IqsYellow, "Yellow")); | ||||
|         } | ||||
|         foreach (KeyValuePair<string, List<string>> keyValuePair in results) | ||||
|             keyValuePair.Value.Sort(); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private void FileFindReplaceAndSave(FileInfo fileInfo, string value) | ||||
|     { | ||||
|         string lines = value.Replace(_AppSettings.IqsRed, "red").Replace(_AppSettings.IqsYellow, "Yellow"); | ||||
|         File.WriteAllText(fileInfo.FullName, lines); | ||||
|     } | ||||
|  | ||||
|     private static string GetWwwRootDirectory() | ||||
|     { | ||||
|         string result; | ||||
|         Assembly assembly = Assembly.GetExecutingAssembly(); | ||||
|         string? assemblyName = assembly.GetName()?.Name; | ||||
|         if (string.IsNullOrEmpty(assemblyName)) | ||||
|             throw new Exception(); | ||||
|         result = Path.Combine(AppContext.BaseDirectory, "wwwroot"); | ||||
|         if (!Directory.Exists(result)) | ||||
|             _ = Directory.CreateDirectory(result); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     Dictionary<string, List<string>> IInfinityQSV4Repository.GetEngineeringSpcReview() | ||||
|     { | ||||
|         Dictionary<string, List<string>>? results; | ||||
|         if (!string.IsNullOrEmpty(_MockRoot)) | ||||
|         { | ||||
|             string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), $"{_RepositoryName}-{nameof(IInfinityQSV4Repository.GetLastGroupIdWithValue)}.json")); | ||||
|             result = new(json); | ||||
|             string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), $"{_RepositoryName}-{nameof(IInfinityQSV4Repository.GetEngineeringSpcReview)}.json")); | ||||
|             results = JsonSerializer.Deserialize<Dictionary<string, List<string>>>(json); | ||||
|             if (results is null) | ||||
|                 throw new NullReferenceException(nameof(results)); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             string commandText = GetCommandText(process, part, test); | ||||
|             result = GetForJsonPath(_DBConnectionFactory, commandText, useIqsConnection: false); | ||||
|             if (result.Length == 0) | ||||
|                 result = new("{}"); | ||||
|             string wwwRootDirectory = GetWwwRootDirectory(); | ||||
|             HttpClient httpClient = _HttpClientFactory.CreateClient(); | ||||
|             FileInfo localFileInfo = new(Path.Combine(wwwRootDirectory, _AppSettings.IqsFileSegments.Last())); | ||||
|             Uri uri = _FileShareRepository.Append(new Uri(_AppSettings.EcMesaFileShareCharacterizationSi), _AppSettings.IqsFileSegments[0..^1]); | ||||
|             List<NginxFileSystemSortable> nginxFileSystemSortableCollection = _FileShareRepository.GetNginxFileSystemSortableCollection(httpClient, uri, ".html"); | ||||
|             if (!localFileInfo.Exists && nginxFileSystemSortableCollection.Count == 0) | ||||
|                 results = new(); | ||||
|             else | ||||
|             { | ||||
|                 string[] lines; | ||||
|                 HttpResponseMessage httpResponseMessage = _FileShareRepository.ReadFile(httpClient, nginxFileSystemSortableCollection[0].Uri); | ||||
|                 Task<string> value = httpResponseMessage.Content.ReadAsStringAsync(); | ||||
|                 value.Wait(); | ||||
|                 if (localFileInfo.Exists && localFileInfo.LastWriteTime == nginxFileSystemSortableCollection[0].DateTime) | ||||
|                     lines = File.ReadAllLines(localFileInfo.FullName); | ||||
|                 else | ||||
|                 { | ||||
|                     FileFindReplaceAndSave(localFileInfo, value.Result); | ||||
|                     lines = value.Result.Split("\r\n"); | ||||
|                 } | ||||
|         return result.ToString(); | ||||
|                 ReadOnlyCollection<Record> records = GetRecords(_AppSettings, lines); | ||||
|                 List<Dictionary<string, Record>> collection = GetCollection(_AppSettings.IqsColumns, records); | ||||
|                 results = GetResults(_AppSettings, collection); | ||||
|             } | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -16,7 +16,6 @@ namespace OI.Metrology.Server.Repositories; | ||||
|  | ||||
| public class MetrologyRepository : IMetrologyRepository | ||||
| { | ||||
|  | ||||
|     private readonly string _MockRoot; | ||||
|     private readonly string _RepositoryName; | ||||
|     private readonly IMemoryCache _MemoryCache; | ||||
| @ -33,8 +32,7 @@ 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) | ||||
|     { | ||||
| @ -58,7 +56,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         return r; | ||||
|     } | ||||
|  | ||||
|     ToolType IMetrologyRepository.GetToolTypeByName(string name) | ||||
|     internal ToolType GetToolTypeByName(string name) | ||||
|     { | ||||
|         ToolType cached; | ||||
|         string cacheKey = "GetToolTypeByName_" + name; | ||||
| @ -109,7 +107,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         return r; | ||||
|     } | ||||
|  | ||||
|     long IMetrologyRepository.InsertToolDataJSON(JToken jsonrow, long headerId, List<ToolTypeMetadata> metaData, string tableName) | ||||
|     internal long InsertToolDataJSON(JToken jsonrow, long headerId, List<ToolTypeMetadata> metaData, string tableName) | ||||
|     { | ||||
|         long r = -1; | ||||
|         using (DbConnection conn = _DBConnectionFactory.GetDbConnection()) | ||||
| @ -117,8 +115,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|             bool isHeader = headerId <= 0; | ||||
|  | ||||
|             // get fields from metadata | ||||
|             List<ToolTypeMetadata> fields = metaData.Where(md => | ||||
|                 md.Header == isHeader).ToList(); | ||||
|             List<ToolTypeMetadata> fields = metaData.Where(md => md.Header == isHeader).ToList(); | ||||
|  | ||||
|             // maps ApiName to ColumnName | ||||
|             Dictionary<string, string> fieldmap = new(); | ||||
| @ -158,8 +155,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|             else | ||||
|             { | ||||
|                 // Find the container field in the json | ||||
|                 JProperty contJP = jsonrow.Children<JProperty>().Where(c => | ||||
|                     string.Equals(c.Name.Trim(), containerField, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); | ||||
|                 JProperty contJP = jsonrow.Children<JProperty>().Where(c => string.Equals(c.Name.Trim(), containerField, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); | ||||
|  | ||||
|                 if ((contJP is not null) && (contJP.Value is JArray array)) | ||||
|                 { | ||||
| @ -186,7 +182,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         _ = cmd.Parameters.Add(p); | ||||
|     } | ||||
|  | ||||
|     private long InsertRowFromJSON( | ||||
|     protected long InsertRowFromJSON( | ||||
|         IDbConnection conn, | ||||
|         string tableName, | ||||
|         JToken jsonrow, | ||||
| @ -266,7 +262,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         return Convert.ToInt64(o); | ||||
|     } | ||||
|  | ||||
|     DataTable IMetrologyRepository.ExportData(string spName, DateTime startTime, DateTime endTime) | ||||
|     internal DataTable ExportData(string spName, DateTime startTime, DateTime endTime) | ||||
|     { | ||||
|         DataTable dt = new(); | ||||
|         DateTime endTimeLocal = endTime.ToLocalTime(); | ||||
| @ -316,7 +312,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         return sb.ToString(); | ||||
|     } | ||||
|  | ||||
|     DataTable IMetrologyRepository.GetHeaders(int toolTypeId, string? startTime, string? endTime, int? pageNo, int? pageSize, long? headerId, out long totalRecords) | ||||
|     internal DataTable 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"); | ||||
|  | ||||
| @ -328,8 +324,7 @@ 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) | ||||
|             ); | ||||
|  | ||||
| @ -406,7 +401,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         return dt; | ||||
|     } | ||||
|  | ||||
|     DataTable IMetrologyRepository.GetData(int toolTypeId, long headerid) | ||||
|     internal DataTable GetData(int toolTypeId, long headerid) | ||||
|     { | ||||
|         ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); | ||||
|  | ||||
| @ -418,9 +413,7 @@ 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) | ||||
|             ); | ||||
|  | ||||
| @ -499,32 +492,17 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         return dt; | ||||
|     } | ||||
|  | ||||
|     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<Guid>(sql, param: new { HeaderID = headerId }); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     void IMetrologyRepository.SetHeaderAttachmentID(int toolTypeId, long headerId, string attachmentId) | ||||
|     internal Guid GetHeaderAttachmentID(int toolTypeId, long headerId) | ||||
|     { | ||||
|         ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); | ||||
|  | ||||
|         using DbConnection conn = _DBConnectionFactory.GetDbConnection(); | ||||
|         string sql = | ||||
|             $"UPDATE [{tt.HeaderTableName}] SET AttachmentID = @AttachmentID WHERE ID = @HeaderID AND AttachmentID IS NULL; " + | ||||
|             $"UPDATE [{tt.HeaderTableName}] SET AttachmentID = NEWID() WHERE ID = @HeaderID AND AttachmentID IS NULL; " + | ||||
|             $"SELECT AttachmentID FROM [{tt.HeaderTableName}] WHERE ID = @HeaderID"; | ||||
|         Guid guid = conn.ExecuteScalar<Guid>(sql, param: new { HeaderID = headerId, AttachmentID = attachmentId }); | ||||
|         if (attachmentId != guid.ToString()) | ||||
|             throw new NotSupportedException($"{attachmentId} != {guid}"); | ||||
|         return conn.ExecuteScalar<Guid>(sql, param: new { HeaderID = headerId }); | ||||
|     } | ||||
|  | ||||
|     string IMetrologyRepository.GetHeaderInsertDate(int toolTypeId, long headerId) | ||||
|     internal string GetHeaderInsertDate(int toolTypeId, long headerId) | ||||
|     { | ||||
|         ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); | ||||
|  | ||||
| @ -533,8 +511,7 @@ 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<string>(sql, param: new { HeaderID = headerId }); | ||||
|     } | ||||
|  | ||||
|     string IMetrologyRepository.GetAttachmentInsertDateByGUID(string tableName, Guid attachmentId) | ||||
|     internal string GetAttachmentInsertDateByGUID(string tableName, Guid attachmentId) | ||||
|     { | ||||
|         using DbConnection conn = _DBConnectionFactory.GetDbConnection(); | ||||
|         string sql = ""; | ||||
| @ -549,27 +526,24 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|  | ||||
|         return conn.ExecuteScalar<string>(sql, param: new { AttachmentID = attachmentId }); | ||||
|     } | ||||
|  | ||||
|     void IMetrologyRepository.SetHeaderDirName(string tableName, long headerId, string dateDir) | ||||
|     internal void 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 }); | ||||
|     } | ||||
|  | ||||
|     void IMetrologyRepository.SetDataAttachmentID(int toolTypeId, long headerId, string title, string attachmentId) | ||||
|     internal Guid GetDataAttachmentID(int toolTypeId, long headerId, string title) | ||||
|     { | ||||
|         ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); | ||||
|  | ||||
|         using DbConnection conn = _DBConnectionFactory.GetDbConnection(); | ||||
|         string sql = | ||||
|             $"UPDATE [{tt.DataTableName}] SET AttachmentID = @AttachmentID WHERE HeaderID = @HeaderID AND Title = @Title AND AttachmentID IS NULL; " + | ||||
|             $"UPDATE [{tt.DataTableName}] SET AttachmentID = NEWID() WHERE HeaderID = @HeaderID AND Title = @Title AND AttachmentID IS NULL; " + | ||||
|             $"SELECT AttachmentID FROM [{tt.DataTableName}] WHERE HeaderID = @HeaderID AND Title = @Title"; | ||||
|         Guid guid = conn.ExecuteScalar<Guid>(sql, param: new { HeaderID = headerId, AttachmentID = attachmentId, Title = title }); | ||||
|         if (attachmentId != guid.ToString()) | ||||
|             throw new NotSupportedException($"{attachmentId} != {guid}"); | ||||
|         return conn.ExecuteScalar<Guid>(sql, param: new { HeaderID = headerId, Title = title }); | ||||
|     } | ||||
|  | ||||
|     string IMetrologyRepository.GetDataInsertDate(int toolTypeId, long headerId, string title) | ||||
|     // J Ouellette Added | ||||
|     internal string GetDataInsertDate(int toolTypeId, long headerId, string title) | ||||
|     { | ||||
|         ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); | ||||
|  | ||||
| @ -586,8 +560,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|  | ||||
|         return conn.ExecuteScalar<string>(sql, param: new { HeaderID = headerId, Title = title }); | ||||
|     } | ||||
|  | ||||
|     void IMetrologyRepository.SetDataDirName(string tableName, long headerId, string title, string dateDir) | ||||
|     internal void SetDataDirName(string tableName, long headerId, string title, string dateDir) | ||||
|     { | ||||
|         using DbConnection conn = _DBConnectionFactory.GetDbConnection(); | ||||
|         string sql = | ||||
| @ -595,13 +568,13 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         _ = conn.Execute(sql, param: new { HeaderID = headerId, Title = title, AttachDirName = dateDir }); | ||||
|     } | ||||
|  | ||||
|     void IMetrologyRepository.PurgeExistingData(int toolTypeId, string title) | ||||
|     internal void PurgeExistingData(int toolTypeId, string title) | ||||
|     { | ||||
|         using DbConnection conn = _DBConnectionFactory.GetDbConnection(); | ||||
|         _ = conn.Execute("PurgeExistingData", param: new { ToolTypeID = toolTypeId, Title = title }, commandType: CommandType.StoredProcedure); | ||||
|     } | ||||
|  | ||||
|     DataSet IMetrologyRepository.GetOIExportData(int toolTypeId, long headerid) | ||||
|     internal DataSet GetOIExportData(int toolTypeId, long headerid) | ||||
|     { | ||||
|         ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); | ||||
|  | ||||
| @ -653,7 +626,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     HeaderCommon[] IMetrologyRepository.GetHeaderTitles(int? toolTypeId, int? pageNo, int? pageSize, out long totalRecords) | ||||
|     internal HeaderCommon[] 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)) | ||||
| @ -689,7 +662,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         return headers; | ||||
|     } | ||||
|  | ||||
|     IEnumerable<KeyValuePair<string, string>> IMetrologyRepository.GetHeaderFields(int toolTypeId, long headerid) | ||||
|     internal IEnumerable<KeyValuePair<string, string>> GetHeaderFields(int toolTypeId, long headerid) | ||||
|     { | ||||
|         ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); | ||||
|  | ||||
| @ -716,9 +689,7 @@ 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) | ||||
| @ -734,7 +705,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         return r; | ||||
|     } | ||||
|  | ||||
|     IEnumerable<AwaitingDisposition> IMetrologyRepository.GetAwaitingDisposition() | ||||
|     internal IEnumerable<AwaitingDisposition> GetAwaitingDisposition() | ||||
|     { | ||||
|         IEnumerable<AwaitingDisposition>? r; | ||||
|         if (!string.IsNullOrEmpty(_MockRoot)) | ||||
| @ -752,7 +723,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         return r; | ||||
|     } | ||||
|  | ||||
|     int IMetrologyRepository.UpdateReviewDate(int toolTypeId, long headerId, bool clearDate) | ||||
|     internal int UpdateReviewDate(int toolTypeId, long headerId, bool clearDate) | ||||
|     { | ||||
|         ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); | ||||
|  | ||||
| @ -772,7 +743,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Guid IMetrologyRepository.GetHeaderAttachmentIDByTitle(int toolTypeId, string title) | ||||
|     internal Guid GetHeaderAttachmentIDByTitle(int toolTypeId, string title) | ||||
|     { | ||||
|         ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); | ||||
|  | ||||
| @ -782,7 +753,7 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         return conn.ExecuteScalar<Guid>(sql, param: new { Title = title }); | ||||
|     } | ||||
|  | ||||
|     Guid IMetrologyRepository.GetDataAttachmentIDByTitle(int toolTypeId, string title) | ||||
|     internal Guid GetDataAttachmentIDByTitle(int toolTypeId, string title) | ||||
|     { | ||||
|         ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); | ||||
|  | ||||
| @ -792,13 +763,31 @@ public class MetrologyRepository : IMetrologyRepository | ||||
|         return conn.ExecuteScalar<Guid>(sql, param: new { Title = title }); | ||||
|     } | ||||
|  | ||||
|     IEnumerable<ToolType> IMetrologyRepository.GetToolTypes() => | ||||
|         GetToolTypes(); | ||||
|     ToolType IMetrologyRepository.GetToolTypeByID(int id) => | ||||
|         GetToolTypeByID(id); | ||||
|     TransactionScope IMetrologyRepository.StartTransaction() => | ||||
|         StartTransaction(); | ||||
|     IEnumerable<ToolTypeMetadata> IMetrologyRepository.GetToolTypeMetadataByToolTypeID(int id) => | ||||
|         GetToolTypeMetadataByToolTypeID(id); | ||||
|     DataTable IMetrologyRepository.GetDataSharePoint(int toolTypeId, string headerId) => throw new NotImplementedException(); | ||||
|  | ||||
|     IEnumerable<ToolType> IMetrologyRepository.GetToolTypes() => GetToolTypes(); | ||||
|     ToolType IMetrologyRepository.GetToolTypeByName(string name) => GetToolTypeByName(name); | ||||
|     ToolType IMetrologyRepository.GetToolTypeByID(int id) => GetToolTypeByID(id); | ||||
|     IEnumerable<ToolTypeMetadata> 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<ToolTypeMetadata> 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<KeyValuePair<string, string>> IMetrologyRepository.GetHeaderFields(int toolTypeId, long headerid) => GetHeaderFields(toolTypeId, headerid); | ||||
|     IEnumerable<AwaitingDisposition> IMetrologyRepository.GetAwaitingDisposition() => GetAwaitingDisposition(); | ||||
|     int IMetrologyRepository.UpdateReviewDate(int toolTypeId, long headerId, bool clearDate) => UpdateReviewDate(toolTypeId, headerId, clearDate); | ||||
|  | ||||
| } | ||||
| @ -3,6 +3,7 @@ using OI.Metrology.Shared.DataModels; | ||||
| using OI.Metrology.Shared.Models.Stateless; | ||||
| using OI.Metrology.Shared.Services; | ||||
| using System.Data; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
|  | ||||
| namespace OI.Metrology.Server.Repository; | ||||
| @ -236,4 +237,96 @@ public class ToolTypesRepository : IToolTypesRepository | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     Result<DataTable> IToolTypesRepository.GetExportData(IMetrologyRepository metrologyRepository, int toolTypeId, string? datebegin, string? dateend) | ||||
|     { | ||||
|         Result<DataTable>? r; | ||||
|         if (!string.IsNullOrEmpty(_MockRoot)) | ||||
|         { | ||||
|             string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), $"{_RepositoryName}-{nameof(IToolTypesRepository.GetExportData)}.json")); | ||||
|             r = Newtonsoft.Json.JsonConvert.DeserializeObject<Result<DataTable>>(json); | ||||
|             if (r is null) | ||||
|                 throw new NullReferenceException(nameof(r)); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             DateTime dateEnd = dateend is null ? DateTime.Now : DateTime.Parse(dateend); | ||||
|             DateTime dateBegin = datebegin is null ? dateEnd.AddMonths(-1) : DateTime.Parse(datebegin); | ||||
|             ToolType tt = metrologyRepository.GetToolTypeByID(toolTypeId); | ||||
|             if (string.IsNullOrEmpty(tt.ExportSPName)) | ||||
|                 throw new NullReferenceException(nameof(tt.ExportSPName)); | ||||
|             DataTable dataTable = metrologyRepository.ExportData(tt.ExportSPName, dateBegin, dateEnd); | ||||
|             r = new() | ||||
|             { | ||||
|                 Results = dataTable, | ||||
|                 TotalRows = dataTable.Rows.Count, | ||||
|             }; | ||||
|         } | ||||
|         return r; | ||||
|     } | ||||
|  | ||||
|     protected static string FormatForCSV(string v) | ||||
|     { | ||||
|         StringBuilder result = new(v.Length + 2); | ||||
|         bool doubleQuoted = false; | ||||
|         if (v.StartsWith(' ') || v.EndsWith(' ') || v.Contains(',') || v.Contains('"')) | ||||
|         { | ||||
|             _ = result.Append('"'); | ||||
|             doubleQuoted = true; | ||||
|         } | ||||
|         foreach (char c in v) | ||||
|         { | ||||
|             _ = c switch | ||||
|             { | ||||
|                 '\r' or '\n' => result.Append(' '), | ||||
|                 '"' => result.Append("\"\""), | ||||
|                 _ => result.Append(c), | ||||
|             }; | ||||
|         } | ||||
|         if (doubleQuoted) | ||||
|             _ = result.Append('"'); | ||||
|         return result.ToString(); | ||||
|     } | ||||
|  | ||||
|     protected static string GetColumnHeaders(DataTable dataTable) | ||||
|     { | ||||
|         StringBuilder result = new(); | ||||
|         for (int i = 0; i < dataTable.Columns.Count; i++) | ||||
|         { | ||||
|             if (i > 0) | ||||
|                 _ = result.Append(','); | ||||
|             _ = result.Append(FormatForCSV(dataTable.Columns[i].ColumnName.TrimEnd('_'))); | ||||
|         } | ||||
|         return result.ToString(); | ||||
|     } | ||||
|  | ||||
|     protected static string GetRowData(DataRow dr) | ||||
|     { | ||||
|         StringBuilder result = new(); | ||||
|         for (int i = 0; i < dr.Table.Columns.Count; i++) | ||||
|         { | ||||
|             if (i > 0) | ||||
|                 _ = result.Append(','); | ||||
|             object v = dr[i]; | ||||
|             if (v is not null && !Convert.IsDBNull(v)) | ||||
|                 _ = result.Append(FormatForCSV(string.Concat(Convert.ToString(v)))); | ||||
|         } | ||||
|         return result.ToString(); | ||||
|     } | ||||
|  | ||||
|     byte[] IToolTypesRepository.GetCSVExport(IMetrologyRepository metrologyRepository, int toolTypeId, string? datebegin, string? dateend) | ||||
|     { | ||||
|         byte[] results; | ||||
|         Result<DataTable> result; | ||||
|         IToolTypesRepository repository = this; | ||||
|         result = repository.GetExportData(metrologyRepository, toolTypeId, datebegin, dateend); | ||||
|         if (result.Results is null) | ||||
|             throw new NullReferenceException(nameof(result.Results)); | ||||
|         StringBuilder stringBuilder = new(); | ||||
|         _ = stringBuilder.AppendLine(GetColumnHeaders(result.Results)); | ||||
|         foreach (DataRow dr in result.Results.Rows) | ||||
|             _ = stringBuilder.AppendLine(GetRowData(dr)); | ||||
|         results = Encoding.UTF8.GetBytes(stringBuilder.ToString()); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										211
									
								
								Server/Repositories/WaferCounterRepository.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,211 @@ | ||||
| using OI.Metrology.Server.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; | ||||
|  | ||||
| public class WaferCounterRepository : IWaferCounterRepository | ||||
| { | ||||
|  | ||||
|     private record Record(int Check, | ||||
|                           int Total, | ||||
|                           string? SlotMap); | ||||
|  | ||||
|     private readonly string _MockRoot; | ||||
|     private readonly string _RepositoryName; | ||||
|     private readonly AppSettings _AppSettings; | ||||
|     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) | ||||
|     { | ||||
|         _AppSettings = appSettings; | ||||
|         _MockRoot = appSettings.MockRoot; | ||||
|         _HttpClientFactory = httpClientFactory; | ||||
|         _DBConnectionFactory = dbConnectionFactory; | ||||
|         _FileShareRepository = fileShareRepository; | ||||
|         _RepositoryName = nameof(WaferCounterRepository)[..^10]; | ||||
|         _OpenInsightApplicationProgrammingInterface = appSettings.OpenInsightApplicationProgrammingInterface; | ||||
|     } | ||||
|  | ||||
|     private void MoveFile(string waferSizeDirectory, NginxFileSystemSortable nginxFileSystemSortable) | ||||
|     { | ||||
|         Calendar calendar = new CultureInfo("en-US").Calendar; | ||||
|         string from = Path.Combine(waferSizeDirectory, nginxFileSystemSortable.Name); | ||||
|         string weekOfYear = $"{nginxFileSystemSortable.DateTime:yyyy}_Week_{calendar.GetWeekOfYear(nginxFileSystemSortable.DateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}"; | ||||
|         string to = Path.Combine(waferSizeDirectory, "Archive", weekOfYear, nginxFileSystemSortable.Name); | ||||
|         _FileShareRepository.MoveFile(from, to); | ||||
|     } | ||||
|  | ||||
|     private static Record GetRecord(string line1, string line2) | ||||
|     { | ||||
|         Record result; | ||||
|         string? waferMap = string.IsNullOrEmpty(line2) || line2.Length != 8 ? null : line2.Substring(1, 1); | ||||
|         int check = waferMap == "1" ? 1 : 0; | ||||
|         int total = int.Parse(line1[1..]); | ||||
|         // string wafers = Array.from(line2[2..]); | ||||
|         foreach (char item in line2[2..]) | ||||
|         { | ||||
|             switch (item) | ||||
|             { | ||||
|                 case '0': | ||||
|                     check += 0; | ||||
|                     waferMap += "0000"; | ||||
|                     break; | ||||
|                 case '1': | ||||
|                     check += 1; | ||||
|                     waferMap += "0001"; | ||||
|                     break; | ||||
|                 case '2': | ||||
|                     check += 1; | ||||
|                     waferMap += "0010"; | ||||
|                     break; | ||||
|                 case '3': | ||||
|                     check += 2; | ||||
|                     waferMap += "0011"; | ||||
|                     break; | ||||
|                 case '4': | ||||
|                     check += 1; | ||||
|                     waferMap += "0100"; | ||||
|                     break; | ||||
|                 case '5': | ||||
|                     check += 2; | ||||
|                     waferMap += "0101"; | ||||
|                     break; | ||||
|                 case '6': | ||||
|                     check += 2; | ||||
|                     waferMap += "0110"; | ||||
|                     break; | ||||
|                 case '7': | ||||
|                     check += 3; | ||||
|                     waferMap += "0111"; | ||||
|                     break; | ||||
|                 case '8': | ||||
|                     check += 1; | ||||
|                     waferMap += "1000"; | ||||
|                     break; | ||||
|                 case '9': | ||||
|                     check += 2; | ||||
|                     waferMap += "1001"; | ||||
|                     break; | ||||
|                 case 'A': | ||||
|                     check += 2; | ||||
|                     waferMap += "1010"; | ||||
|                     break; | ||||
|                 case 'B': | ||||
|                     check += 3; | ||||
|                     waferMap += "1011"; | ||||
|                     break; | ||||
|                 case 'C': | ||||
|                     check += 2; | ||||
|                     waferMap += "1100"; | ||||
|                     break; | ||||
|                 case 'D': | ||||
|                     check += 3; | ||||
|                     waferMap += "1101"; | ||||
|                     break; | ||||
|                 case 'E': | ||||
|                     check += 3; | ||||
|                     waferMap += "1110"; | ||||
|                     break; | ||||
|                 case 'F': | ||||
|                     check += 4; | ||||
|                     waferMap += "1111"; | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|         result = new(check, total, waferMap); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private Uri GetWaferSizeUri(string area, string waferSize) => | ||||
|         _FileShareRepository.Append(new Uri(_AppSettings.EcMesaFileShareCharacterizationSi), "WaferCounter", area, waferSize); | ||||
|  | ||||
|     private string GetWaferSizeDirectory(string area, string waferSize, bool destination) => | ||||
|         destination ? Path.Combine(_AppSettings.WaferCounterDestinationDirectory, area, waferSize) : Path.Combine(_AppSettings.EcCharacterizationSi, "WaferCounter", area, waferSize); | ||||
|  | ||||
|     string? IWaferCounterRepository.GetSlotMap(string line1, string line2) => | ||||
|         GetRecord(line1, line2).SlotMap; | ||||
|  | ||||
|     private List<NginxFileSystemSortable> GetNginxFileSystemSortableCollection(HttpClient httpClient, Uri waferSizeUri, string waferSizeDirectory) | ||||
|     { | ||||
|         List<NginxFileSystemSortable> results = new(); | ||||
|         DateTime dateTime = DateTime.Now; | ||||
|         long ticks = dateTime.AddSeconds(_AppSettings.WaferCounterTwoFileSecondsWait).Ticks; | ||||
|         for (int i = 0; i < int.MaxValue; i++) | ||||
|         { | ||||
|             results = _FileShareRepository.GetNginxFileSystemSortableCollection(httpClient, waferSizeUri, ".wc"); | ||||
|             if (results.Count > 1 || DateTime.Now.Ticks > ticks) | ||||
|                 break; | ||||
|             Thread.Sleep(250); | ||||
|         } | ||||
|         for (int i = 1; i < results.Count; i++) | ||||
|             MoveFile(waferSizeDirectory, results[i]); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private static WaferCounter GetLastQuantityAndSlotMapWithText(string waferSize, string text, HttpClient httpClient, NginxFileSystemSortable nginxFileSystemSortable) | ||||
|     { | ||||
|         WaferCounter result; | ||||
|         Task<string> value = httpClient.GetStringAsync(nginxFileSystemSortable.Uri); | ||||
|         value.Wait(); | ||||
|         string[] lines = value.Result.Split("\r\n"); | ||||
|         if (lines.Length <= 1) | ||||
|             throw new Exception("Incomplete file length!"); | ||||
|         string[] segments = nginxFileSystemSortable.Name.Split('-'); | ||||
|         Record record = GetRecord(lines[0], lines[1]); | ||||
|         string equipmentId = segments.Length <= 1 ? nginxFileSystemSortable.Name : segments[1].Split('.')[0]; | ||||
|         if (string.IsNullOrEmpty(record.SlotMap) || record.SlotMap.Length != 25) | ||||
|             throw new Exception("Wrong length!"); | ||||
|         if (record.Total != record.Check) | ||||
|             throw new Exception("Invalid!"); | ||||
|         result = new(nginxFileSystemSortable.DateTime, nginxFileSystemSortable.DateTime.ToString("yyyy-MM-dd hh:mm tt"), $"WC{waferSize}{equipmentId}", text, record.Total, record.SlotMap); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     WaferCounter IWaferCounterRepository.GetLastQuantityAndSlotMap(string area, string waferSize) | ||||
|     { | ||||
|         WaferCounter result; | ||||
|         Uri waferSizeUri = GetWaferSizeUri(area, waferSize); | ||||
|         HttpClient httpClient = _HttpClientFactory.CreateClient(); | ||||
|         string waferSizeDirectory = GetWaferSizeDirectory(area, waferSize, destination: false); | ||||
|         List<NginxFileSystemSortable> nginxFileSystemSortableCollection = GetNginxFileSystemSortableCollection(httpClient, waferSizeUri, waferSizeDirectory); | ||||
|         if (nginxFileSystemSortableCollection.Count < 1) | ||||
|             throw new Exception("No files!"); | ||||
|         string text = string.Empty; | ||||
|         result = GetLastQuantityAndSlotMapWithText(waferSize, text, httpClient, nginxFileSystemSortableCollection[0]); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private void Save(string waferSizeDestinationDirectory, string area, string waferSize, string text, NginxFileSystemSortable nginxFileSystemSortable, WaferCounter result) => | ||||
|         _FileShareRepository.FileWrite(Path.Combine(waferSizeDestinationDirectory, $"{nginxFileSystemSortable.Name}.csv"), $"100,{waferSize},{area},{nginxFileSystemSortable.DateTime},{text},{result.Total:00},{result.SlotMap} "); | ||||
|  | ||||
|     WaferCounter IWaferCounterRepository.GetLastQuantityAndSlotMapWithText(string area, string waferSize, string text) | ||||
|     { | ||||
|         WaferCounter result; | ||||
|         Uri waferSizeUri = GetWaferSizeUri(area, waferSize); | ||||
|         HttpClient httpClient = _HttpClientFactory.CreateClient(); | ||||
|         string waferSizeDirectory = GetWaferSizeDirectory(area, waferSize, destination: false); | ||||
|         List<NginxFileSystemSortable> nginxFileSystemSortableCollection = GetNginxFileSystemSortableCollection(httpClient, waferSizeUri, waferSizeDirectory); | ||||
|         if (nginxFileSystemSortableCollection.Count < 1) | ||||
|             throw new Exception("No files!"); | ||||
|         result = GetLastQuantityAndSlotMapWithText(waferSize, text, httpClient, nginxFileSystemSortableCollection[0]); | ||||
|         if (result is not null) | ||||
|         { | ||||
|             string waferSizeDestinationDirectory = _AppSettings.WaferCounterDestinationDirectory; | ||||
|             // string waferSizeDestinationDirectory = GetWaferSizeUri(area, waferSize, destination: true); | ||||
|             Save(waferSizeDestinationDirectory, area, waferSize, text, nginxFileSystemSortableCollection[0], result); | ||||
|         } | ||||
|         if (result is null) | ||||
|             throw new NullReferenceException(nameof(result)); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -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, "MetrologyAttachments", $"{tableName}_", year, $"WW{weekNum:00}", attachmentId.ToString(), filename); | ||||
|         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) | ||||
|             throw new Exception("File not found!"); | ||||
| @ -77,36 +77,63 @@ public class AttachmentsService : IAttachmentsService | ||||
|         return GetAttachmentStream(tableName, attachmentId, filename); | ||||
|     } | ||||
|  | ||||
|     void IAttachmentsService.SaveAttachment(ToolType toolType, Attachment attachment) | ||||
|     private void SaveAttachment(ToolType toolType, long headerId, string dataUniqueId, string filename, IFormFile uploadedFile) | ||||
|     { | ||||
|         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.DestinationFileName is null) | ||||
|             throw new NullReferenceException($"{nameof(attachment.DestinationFileName)}"); | ||||
|         if (string.IsNullOrWhiteSpace(attachment.UniqueId)) | ||||
|             throw new NullReferenceException($"{nameof(attachment.UniqueId)}"); | ||||
|         if (attachment.DestinationFileName != "Image.pdf") | ||||
|             _MetrologyRepository.SetHeaderAttachmentID(toolType.ID, attachment.HeaderId.Value, attachment.AttachmentId); | ||||
|  | ||||
|         using System.Transactions.TransactionScope trans = _MetrologyRepository.StartTransaction(); | ||||
|         Guid attachmentId = Guid.Empty; | ||||
|         DateTime insertDate = new(); | ||||
|         string? tableName = ""; | ||||
|         if (string.IsNullOrWhiteSpace(dataUniqueId)) | ||||
|         { | ||||
|             attachmentId = _MetrologyRepository.GetHeaderAttachmentID(toolType.ID, headerId); | ||||
|             insertDate = Convert.ToDateTime(_MetrologyRepository.GetHeaderInsertDate(toolType.ID, headerId)); | ||||
|             tableName = toolType.HeaderTableName; | ||||
|         } | ||||
|         else | ||||
|             _MetrologyRepository.SetDataAttachmentID(toolType.ID, attachment.HeaderId.Value, attachment.UniqueId, attachment.AttachmentId); | ||||
|         { | ||||
|             attachmentId = _MetrologyRepository.GetDataAttachmentID(toolType.ID, headerId, dataUniqueId); | ||||
|             insertDate = Convert.ToDateTime(_MetrologyRepository.GetDataInsertDate(toolType.ID, headerId, dataUniqueId)); | ||||
|             // Get Date for new directory name | ||||
|             tableName = toolType.DataTableName; | ||||
|         } | ||||
|         if (Equals(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); | ||||
|         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) | ||||
|         { | ||||
|             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; | ||||
|         int weekNum; | ||||
|         string file; | ||||
|         string year; | ||||
|         string directory; | ||||
|         Task<string> json; | ||||
|         Uri weekDirectory; | ||||
|         Uri checkDirectory; | ||||
|         List<string> files = new(); | ||||
|         string[] collection = ["-", string.Empty]; | ||||
|         NginxFileSystem[]? nginxFileSystemCollection; | ||||
|         Task<HttpResponseMessage> httpResponseMessage; | ||||
|         HttpClient httpClient = _HttpClientFactory.CreateClient(); | ||||
| @ -114,14 +141,11 @@ public class AttachmentsService : IAttachmentsService | ||||
|         DateTime[] dateTimes = new DateTime[] { DateTime.Now, DateTime.Now.AddDays(-6.66) }; | ||||
|         ToolType toolType = metrologyRepository.GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); | ||||
|         foreach (DateTime dateTime in dateTimes) | ||||
|         { | ||||
|             foreach (string item in collection) | ||||
|         { | ||||
|             year = dateTime.Year.ToString(); | ||||
|             weekNum = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); | ||||
|                 weekDirectory = _FileShareRepository.Append(mesaFileShareMetrologySi, "MetrologyAttachments", $"{toolType.HeaderTableName}_", year, $"WW{weekNum:00}"); | ||||
|                 checkDirectory = _FileShareRepository.Append(weekDirectory, $"{item}{headerId}"); | ||||
|                 directory = Path.Combine(_AppSettings.EcMetrologySi, "MetrologyAttachments", $"{toolType.HeaderTableName}_", year, $"WW{weekNum:00}", $"{item}{headerId}"); | ||||
|             weekDirectory = _FileShareRepository.Append(mesaFileShareMetrologySi, $"{toolType.HeaderTableName}_", year, $"WW{weekNum:00}"); | ||||
|             checkDirectory = _FileShareRepository.Append(weekDirectory, headerId.ToString()); | ||||
|             httpResponseMessage = httpClient.GetAsync(checkDirectory); | ||||
|             httpResponseMessage.Wait(); | ||||
|             if (httpResponseMessage.Result.StatusCode != System.Net.HttpStatusCode.OK) | ||||
| @ -132,15 +156,7 @@ public class AttachmentsService : IAttachmentsService | ||||
|             if (nginxFileSystemCollection is null) | ||||
|                 continue; | ||||
|             foreach (NginxFileSystem nginxFileSystem in nginxFileSystemCollection) | ||||
|                 { | ||||
|                     if (!nginxFileSystem.Name.EndsWith(".pdsf")) | ||||
|                         continue; | ||||
|                     file = Path.Combine(directory, nginxFileSystem.Name); | ||||
|                     files.Add(file); | ||||
|                 } | ||||
|                 if (files.Count != 0) | ||||
|                     break; | ||||
|             } | ||||
|                 files.Add(_FileShareRepository.Append(checkDirectory, nginxFileSystem.Name).AbsoluteUri); | ||||
|             if (files.Count != 0) | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
							
								
								
									
										96
									
								
								Server/Views/Export/Index.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,96 @@ | ||||
| @using OI.Metrology.Shared.ViewModels | ||||
| @model OI.Metrology.Shared.ViewModels.Export | ||||
| @{ | ||||
|     ViewData["Title"] = "Export Data"; | ||||
| } | ||||
|  | ||||
| <style> | ||||
|     td { | ||||
|         padding-bottom: 2em; | ||||
|         padding-right: 1em; | ||||
|     } | ||||
| </style> | ||||
|  | ||||
| <h3>Export Data</h3> | ||||
|  | ||||
| <hr /> | ||||
|  | ||||
| <form asp-controller="Export" asp-action="ExportData" method="post" class="form-inline"> | ||||
|     <div class="form-group"> | ||||
|         <label for="ToolType">Tool Type</label> | ||||
|         <div class="form-control" id="ToolType" name="ToolType"></div> | ||||
|         @Html.ValidationMessage("ToolType", new { @class = "text-danger" }) | ||||
|     </div> | ||||
|     <div class="form-group"> | ||||
|         <label for="StartDate">Start Time</label> | ||||
|         <div class="form-control mb-2 mr-sm-2" id="StartDateControl"></div> | ||||
|         <div class="form-control mb-2 mr-sm-2" id="StartTimeControl"></div> | ||||
|         @Html.ValidationMessage("StartDate", new { @class = "text-danger" }) | ||||
|     </div> | ||||
|     <div class="form-group"> | ||||
|         <label for="EndDate">End Time</label> | ||||
|         <div class="form-control mb-2 mr-sm-2" id="EndDateControl"></div> | ||||
|         <div class="form-control mb-2 mr-sm-2" id="EndTimeControl"></div> | ||||
|         @Html.ValidationMessage("EndDate", new { @class = "text-danger" }) | ||||
|     </div> | ||||
|     <div class="form-group"> | ||||
|         <button type="submit" class="btn btn-primary">Export Data</button> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-group"> | ||||
|         @Html.ValidationMessage("Exception", new { @class = "text-danger" }) | ||||
|     </div> | ||||
|     <div asp-validation-summary="ModelOnly" class="text-danger"></div> | ||||
|  | ||||
| </form> | ||||
|  | ||||
| <script> | ||||
|  | ||||
|     $(document).ready(function () { | ||||
|          | ||||
|         var apiUrl = "@ViewBag.ApiUrl"; | ||||
|          | ||||
|         $("#ToolType").igCombo({ | ||||
|             dataSource: apiUrl + '/tooltypes', | ||||
|             responseDataKey: "Results", | ||||
|             textKey: "ToolTypeName", | ||||
|             valueKey: "ID", | ||||
|             mode: "dropdown", | ||||
|             width: 150 | ||||
|         }); | ||||
|  | ||||
|         var startTime = new Date("@Model.StartTime.ToString("yyyy-MM-ddTHH:mm")"); | ||||
|  | ||||
|         $("#StartDateControl").igDatePicker({ | ||||
|             dateInputFormat: "date", | ||||
|             value: startTime, | ||||
|             width: 125, | ||||
|             inputName: "StartDate", | ||||
|         }); | ||||
|  | ||||
|         $("#StartTimeControl").igTimePicker({ | ||||
|             dateInputFormat: "time", | ||||
|             value: startTime, | ||||
|             width: 110, | ||||
|             inputName: "StartTime", | ||||
|         }); | ||||
|  | ||||
|         var endTime = new Date("@Model.EndTime.ToString("yyyy-MM-ddTHH:mm")"); | ||||
|  | ||||
|         $("#EndDateControl").igDatePicker({ | ||||
|             dateInputFormat: "date", | ||||
|             value: endTime, | ||||
|             width: 125, | ||||
|             inputName: "EndDate", | ||||
|         }); | ||||
|  | ||||
|         $("#EndTimeControl").igTimePicker({ | ||||
|             dateInputFormat: "time", | ||||
|             value: endTime, | ||||
|             width: 110, | ||||
|             inputName: "EndTime", | ||||
|         }); | ||||
|  | ||||
|     }); | ||||
|  | ||||
| </script> | ||||
							
								
								
									
										28
									
								
								Server/Views/Pages/AwaitingDispo.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,28 @@ | ||||
| @{ | ||||
|     ViewData["Title"] = "Awaiting Disposition"; | ||||
| } | ||||
|  | ||||
| <h4>Awaiting Disposition</h4> | ||||
|  | ||||
| <div style="height: 450px;"> | ||||
|     <table id="grid"></table> | ||||
| </div> | ||||
|  | ||||
| <div class="row" style="margin-top: 10px; margin-bottom: 20px;"> | ||||
|     <div class="col-xs-1"> | ||||
|         <input type="button" id="OpenButton" value="Open" /> | ||||
|     </div> | ||||
|     <div class="col-xs-1"> | ||||
|         <input type="button" id="RefreshButton" value="Refresh" /> | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
| <script> | ||||
|  | ||||
|     $(document).ready(function () { | ||||
|  | ||||
|         initAwaitingDisposition("@ViewBag.ApiUrl"); | ||||
|  | ||||
|     }); | ||||
|  | ||||
| </script> | ||||
							
								
								
									
										3
									
								
								Server/Views/Pages/Home.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,3 @@ | ||||
| @{ | ||||
|     ViewData["Title"] = "Metrology Home Page"; | ||||
| } | ||||
							
								
								
									
										57
									
								
								Server/Views/Pages/RunHeaders.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,57 @@ | ||||
| @{ | ||||
|     ViewData["Title"] = "Run Headers"; | ||||
| } | ||||
|  | ||||
| <style> | ||||
|     html, | ||||
|     body { | ||||
|         height: 100%; | ||||
|     } | ||||
|  | ||||
|     div.container-fluid { | ||||
|         height: 90%; | ||||
|     } | ||||
|  | ||||
|     #HeaderGrid, | ||||
|     #FieldsGrid { | ||||
|         font-size: 12px; | ||||
|     } | ||||
|  | ||||
|     .FieldTitle { | ||||
|         font-weight: bold; | ||||
|     } | ||||
| </style> | ||||
|  | ||||
| <h4>Run Headers</h4> | ||||
|  | ||||
| <table> | ||||
|     <tr> | ||||
|         <td> | ||||
|             <label for="ToolType">Tool Type:</label> | ||||
|         </td> | ||||
|         <td> | ||||
|             <div id="ToolType"></div> | ||||
|         </td> | ||||
|     </tr> | ||||
| </table> | ||||
|  | ||||
| <table width="100%" height="80%"> | ||||
|     <tr> | ||||
|         <td width="50%"> | ||||
|             <table id="HeaderGrid"></table> | ||||
|         </td> | ||||
|         <td width="50%"> | ||||
|             <table id="FieldsGrid"></table> | ||||
|         </td> | ||||
|     </tr> | ||||
| </table> | ||||
|  | ||||
| <script> | ||||
|  | ||||
|     $(document).ready(function () { | ||||
|  | ||||
|         initRunHeaders("@ViewBag.ApiUrl"); | ||||
|  | ||||
|     }); | ||||
|  | ||||
| </script> | ||||
							
								
								
									
										104
									
								
								Server/Views/Pages/RunInfo.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,104 @@ | ||||
| @model OI.Metrology.Shared.ViewModels.RunInfo | ||||
| @{ | ||||
|     ViewData["Title"] = "Run Information"; | ||||
| } | ||||
| <style> | ||||
|     #HeaderGridDiv, | ||||
|     #DetailsGridDiv { | ||||
|         font-size: 12px; | ||||
|     } | ||||
| </style> | ||||
|  | ||||
| <h4>Run Information</h4> | ||||
|  | ||||
| <form class="form-inline mb-4"> | ||||
|     <div class="form-group"> | ||||
|         <label for="ToolType">Tool Type</label> | ||||
|         <div class="form-control" id="ToolType"></div> | ||||
|     </div> | ||||
|     <div class="form-group"> | ||||
|         <label for="StartDate">Start Time</label> | ||||
|         <div class="form-control mb-2 mr-sm-2" id="StartDate"></div> | ||||
|         <div class="form-control mb-2 mr-sm-2" id="StartTime"></div> | ||||
|     </div> | ||||
|     <div class="form-group"> | ||||
|         <label for="EndDate">End Time</label> | ||||
|         <div class="form-control mb-2 mr-sm-2" id="EndDate"></div> | ||||
|         <div class="form-control mb-2 mr-sm-2" id="EndTime"></div> | ||||
|     </div>   | ||||
|     <div class="form-group"> | ||||
|         <input class="btn btn-primary" type="button" value="Load Headers" id="LoadHeadersButton" /> | ||||
|     </div>   | ||||
|     <div class="form-group"> | ||||
|         <label class="form-check-label" for="chkAutoRefresh"> | ||||
|             Auto-Refresh | ||||
|         </label> | ||||
|         <input class="form-check-input" type="checkbox" id="chkAutoRefresh"> | ||||
|     </div> | ||||
|     <div class="form-group"> | ||||
|         <label class="form-check-label" for="chkCopyOnGet"> | ||||
|             Copy-On-Get | ||||
|         </label> | ||||
|         <input class="form-check-input" type="checkbox" id="chkCopyOnGet"> | ||||
|     </div> | ||||
| </form> | ||||
|  | ||||
| <div style="height: 300px;" id="HeaderGridDiv"> | ||||
|     <span id="ToolTypeID" hidden></span> | ||||
|     <table id="HeaderGrid"></table> | ||||
| </div> | ||||
|  | ||||
| <div class="row" style="margin-top: 10px; margin-bottom: 20px;"> | ||||
|     <div class="col-xs-1"> | ||||
|         <input type="button" class="btn" id="GetDataButton" value="Get Data" disabled /> | ||||
|     </div> | ||||
|     <div class="col-xs-1"> | ||||
|         <input type="button" class="btn" id="ReviewButton" value="Review" disabled /> | ||||
|     </div> | ||||
|     <div class="col-xs-1"> | ||||
|         <input type="button" class="btn" id="RecipeParametersButton" value="Parameters" disabled /> | ||||
|     </div> | ||||
|     <div class="col-xs-1"> | ||||
|         <input type="button" class="btn" id="ViewButton" value="View" disabled /> | ||||
|     </div> | ||||
|     <div class="col-xs-1"> | ||||
|         <input type="button" class="btn" id="PinButton" value="Pin" disabled /> | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
| <div id="DetailsDiv" hidden> | ||||
|  | ||||
|     <span id="HeaderId" hidden></span> | ||||
|     <span id="HeaderAttachmentId" hidden></span> | ||||
|     <div style="padding-bottom: 20px;" id="DetailsGridDiv"> | ||||
|         <table id="DetailsGrid"></table> | ||||
|     </div> | ||||
|  | ||||
|     <div id="ExportDiv" style="margin-top: 10px;" hidden> | ||||
|         <input type="button" value="Send to OpenInsight" id="OIExportButton" /> | ||||
|         <span id="OIExportResult" style="margin-left: 10px; font-weight: bold; color: #366b02;"></span> | ||||
|     </div> | ||||
|  | ||||
|     <p style="margin-top: 20px;"> | ||||
|         <iframe id="DataAttachmentFrame" style="height:900px; border-width:thin; margin-right: 10px;" hidden></iframe> | ||||
|         <iframe id="HeaderAttachmentFrame" style="height:900px; border-width:thin;" hidden></iframe> | ||||
|           | ||||
|         <div id="DataAttachmentDiv" hidden> | ||||
|             <canvas id="DataAttachmentCanvas"></canvas> | ||||
|         </div> | ||||
|         <div id="HeaderAttachmentDiv" hidden> | ||||
|             <canvas id="HeaderAttachmentCanvas"></canvas> | ||||
|         </div> | ||||
|     </p> | ||||
|  | ||||
| </div> | ||||
|  | ||||
| <script> | ||||
|  | ||||
|     $(document).ready(function () { | ||||
|  | ||||
|         initRunInfo("@ViewBag.ApiUrl", "@Model.ToolTypeID", "@Model.HeaderID", "@Model.HeaderAttachmentID"); | ||||
|  | ||||
|     }); | ||||
|  | ||||
| </script> | ||||
| @ -103,6 +103,6 @@ | ||||
|         $("#runDataSheet5").click(function () { runDataSheet(5, $(this).val()) }); | ||||
|         $("#runDataSheet6").click(function () { runDataSheet(6, $(this).val()) }); | ||||
|         $("#restartButton").click(function () { restartButton() }); | ||||
|         initWorkMaterial("@ViewBag.ApiUrl", ""); | ||||
|         initWorkMaterial("@ViewBag.ApiUrl"); | ||||
|     }); | ||||
| </script> | ||||
|  | ||||
| @ -6,42 +6,36 @@ | ||||
|     <meta name="viewport" content="width=device-width" /> | ||||
|     <title>@ViewBag.Title</title> | ||||
|  | ||||
|     <script src="https://oi-metrology-viewer-prod.mes.infineon.com/js/modernizr-3.6.0-custom.js?no-cache=2024-06-18-10-54" | ||||
|         type="text/javascript"></script> | ||||
|     <script src="~/js/modernizr-3.6.0-custom.js" type="text/javascript" asp-append-version="true"></script> | ||||
|  | ||||
|     <link href="https://oi-metrology-viewer-prod.mes.infineon.com/styles/bootstrap.min.css?no-cache=2024-06-18-10-54" | ||||
|         rel="stylesheet" /> | ||||
|     <link | ||||
|         href="https://oi-metrology-viewer-prod.mes.infineon.com/igniteui/css/themes/bootstrap3/default/infragistics.theme.css?no-cache=2024-06-18-10-54" | ||||
|         rel="stylesheet" /> | ||||
|     <link | ||||
|         href="https://oi-metrology-viewer-prod.mes.infineon.com/igniteui/css/structure/infragistics.css?no-cache=2024-06-18-10-54" | ||||
|         rel="stylesheet" /> | ||||
|     <link href="https://oi-metrology-viewer-prod.mes.infineon.com/styles/site-server.css?no-cache=2024-06-18-10-54" | ||||
|         rel="stylesheet" /> | ||||
|     <link href="https://oi-metrology-viewer-prod.mes.infineon.com/styles/index.css?no-cache=2024-06-18-10-54" | ||||
|         rel="stylesheet" /> | ||||
|     <link href="~/styles/bootstrap.min.css" rel="stylesheet" asp-append-version="true" /> | ||||
|     <link href="~/igniteui/css/themes/bootstrap3/default/infragistics.theme.css" rel="stylesheet" | ||||
|         asp-append-version="true" /> | ||||
|     <link href="~/igniteui/css/structure/infragistics.css" rel="stylesheet" asp-append-version="true" /> | ||||
|     <link href="~/styles/site.css" rel="stylesheet" asp-append-version="true" /> | ||||
|  | ||||
|     <script src="https://oi-metrology-viewer-prod.mes.infineon.com/js/jquery-3.6.0.min.js?no-cache=2024-06-18-10-54" | ||||
|         type="text/javascript"></script> | ||||
|     <script src="https://oi-metrology-viewer-prod.mes.infineon.com/js/jquery-ui.min.js?no-cache=2024-06-18-10-54" | ||||
|         type="text/javascript"></script> | ||||
|     <script | ||||
|         src="https://oi-metrology-viewer-prod.mes.infineon.com/igniteui/js/infragistics.core.js?no-cache=2024-06-18-10-54" | ||||
|         type="text/javascript"></script> | ||||
|     <script | ||||
|         src="https://oi-metrology-viewer-prod.mes.infineon.com/igniteui/js/infragistics.lob.js?no-cache=2024-06-18-10-54" | ||||
|         type="text/javascript"></script> | ||||
|     <script | ||||
|         src="https://oi-metrology-viewer-prod.mes.infineon.com/igniteui/js/infragistics.dv.js?no-cache=2024-06-18-10-54" | ||||
|         type="text/javascript"></script> | ||||
|     <script src="~/js/jquery-3.6.0.min.js" type="text/javascript" asp-append-version="true"></script> | ||||
|     <script src="~/js/jquery-ui.min.js" type="text/javascript" asp-append-version="true"></script> | ||||
|     <script src="~/igniteui/js/infragistics.core.js" type="text/javascript" asp-append-version="true"></script> | ||||
|     <script src="~/igniteui/js/infragistics.lob.js" type="text/javascript" asp-append-version="true"></script> | ||||
|     <script src="~/igniteui/js/infragistics.dv.js" type="text/javascript" asp-append-version="true"></script> | ||||
|  | ||||
|     <script src="https://oi-metrology-viewer-prod.mes.infineon.com/js/chart-4.3.0.min.js" type="module"></script> | ||||
|     <script src="~/js/chart-4.3.0.min.js" type="module"></script> | ||||
|  | ||||
|     <script src="https://oi-metrology-viewer-prod.mes.infineon.com/js/common.js?no-cache=2024-06-18-10-54" | ||||
|         type="text/javascript"></script> | ||||
|     <script src="https://oi-metrology-viewer-prod.mes.infineon.com/js/site-server.js?no-cache=2024-06-18-10-54" | ||||
|         type="text/javascript"></script> | ||||
|     <script src="~/js/common.js" type="text/javascript" asp-append-version="true"></script> | ||||
|     <script src="~/js/site.js" type="text/javascript" asp-append-version="true"></script> | ||||
|     <script> | ||||
|         $(document).ready(function () { | ||||
|             if (location.pathname == "/") { | ||||
|                 route = "/AwaitingDispo"; | ||||
|             } | ||||
|             else { | ||||
|                 route = location.pathname; | ||||
|             } | ||||
|             $('ul.nav.navbar-nav').find('a[href="' + route + '"]') | ||||
|                 .closest('li').addClass('alert-info'); | ||||
|         }); | ||||
|     </script> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
| @ -63,9 +57,7 @@ | ||||
|                     <span class="icon-bar"></span> | ||||
|                 </button> | ||||
|                 <div class="navbar-brand"> | ||||
|                     <a href="https://oi-metrology-viewer-prod.mes.infineon.com"><img | ||||
|                             src="https://oi-metrology-viewer-prod.mes.infineon.com/images/IFX_LOGO_RGB.png" | ||||
|                             height="20" /></a> | ||||
|                     <a href="/"><img src="~/images/IFX_LOGO_RGB.png" height="20" /></a> | ||||
|                     OI Metrology Viewer | ||||
|                 </div> | ||||
|             </div> | ||||
| @ -99,10 +91,8 @@ | ||||
|  | ||||
|     <div id="MessageModal"></div> | ||||
|  | ||||
|     <script src="https://oi-metrology-viewer-prod.mes.infineon.com/js/bootstrap.min.js?no-cache=2024-06-18-10-54" | ||||
|         type="text/javascript"></script> | ||||
|     <script src="https://oi-metrology-viewer-prod.mes.infineon.com/js/respond.min.js?no-cache=2024-06-18-10-54" | ||||
|         type="text/javascript"></script> | ||||
|     <script src="~/js/bootstrap.min.js" type="text/javascript" asp-append-version="true"></script> | ||||
|     <script src="~/js/respond.min.js" type="text/javascript" asp-append-version="true"></script> | ||||
|     @RenderSection("scripts", required: false) | ||||
| </body> | ||||
|  | ||||
|  | ||||
| Before Width: | Height: | Size: 272 KiB After Width: | Height: | Size: 272 KiB | 
| Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB | 
| Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB | 
| Before Width: | Height: | Size: 210 B After Width: | Height: | Size: 210 B | 
| Before Width: | Height: | Size: 225 B After Width: | Height: | Size: 225 B | 
| Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB | 
| Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB | 
| Before Width: | Height: | Size: 418 B After Width: | Height: | Size: 418 B | 
| Before Width: | Height: | Size: 985 B After Width: | Height: | Size: 985 B | 
| Before Width: | Height: | Size: 964 B After Width: | Height: | Size: 964 B | 
| Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 486 B | 
| Before Width: | Height: | Size: 483 B After Width: | Height: | Size: 483 B | 
| Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB | 
| Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB | 
| Before Width: | Height: | Size: 999 B After Width: | Height: | Size: 999 B | 
| Before Width: | Height: | Size: 993 B After Width: | Height: | Size: 993 B | 
| Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB | 
| Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB | 
| Before Width: | Height: | Size: 986 B After Width: | Height: | Size: 986 B | 
| Before Width: | Height: | Size: 1011 B After Width: | Height: | Size: 1011 B | 
| Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB | 
| Before Width: | Height: | Size: 1009 B After Width: | Height: | Size: 1009 B | 
| Before Width: | Height: | Size: 1017 B After Width: | Height: | Size: 1017 B | 
| Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 1010 B After Width: | Height: | Size: 1010 B | 
| Before Width: | Height: | Size: 1006 B After Width: | Height: | Size: 1006 B | 
| Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 995 B | 
| Before Width: | Height: | Size: 1010 B After Width: | Height: | Size: 1010 B | 
| Before Width: | Height: | Size: 1006 B After Width: | Height: | Size: 1006 B | 
| Before Width: | Height: | Size: 976 B After Width: | Height: | Size: 976 B | 
| Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 958 B | 
| Before Width: | Height: | Size: 965 B After Width: | Height: | Size: 965 B | 
| Before Width: | Height: | Size: 965 B After Width: | Height: | Size: 965 B | 
| Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB | 
| Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 980 B After Width: | Height: | Size: 980 B | 
| Before Width: | Height: | Size: 1016 B After Width: | Height: | Size: 1016 B | 
| Before Width: | Height: | Size: 1013 B After Width: | Height: | Size: 1013 B | 
| Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB | 
| Before Width: | Height: | Size: 978 B After Width: | Height: | Size: 978 B | 
| Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB | 
| Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB | 
| Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB | 
| Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB | 
| Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB | 
| Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB | 
| Before Width: | Height: | Size: 638 B After Width: | Height: | Size: 638 B | 
| Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 280 B | 
| Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 307 B | 
| Before Width: | Height: | Size: 318 B After Width: | Height: | Size: 318 B | 
| Before Width: | Height: | Size: 300 B After Width: | Height: | Size: 300 B | 
| Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |