diff --git a/Adaptation/.editorconfig b/Adaptation/.editorconfig index b22ed15..e393d5b 100644 --- a/Adaptation/.editorconfig +++ b/Adaptation/.editorconfig @@ -110,7 +110,7 @@ dotnet_diagnostic.CA2254.severity = none # CA2254: The logging message template dotnet_diagnostic.IDE0001.severity = warning # IDE0001: Simplify name dotnet_diagnostic.IDE0002.severity = warning # Simplify (member access) - System.Version.Equals("1", "2"); Version.Equals("1", "2"); dotnet_diagnostic.IDE0004.severity = warning # IDE0004: Cast is redundant. -dotnet_diagnostic.IDE0005.severity = warning # Using directive is unnecessary +dotnet_diagnostic.IDE0005.severity = none # Using directive is unnecessary dotnet_diagnostic.IDE0028.severity = none # IDE0028: Collection initialization can be simplified dotnet_diagnostic.IDE0031.severity = warning # Use null propagation (IDE0031) dotnet_diagnostic.IDE0047.severity = warning # IDE0047: Parentheses can be removed @@ -122,6 +122,7 @@ dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [Distance]cs dotnet_diagnostic.IDE0300.severity = none # IDE0300: Collection initialization can be simplified dotnet_diagnostic.IDE0301.severity = none #IDE0301: Collection initialization can be simplified dotnet_diagnostic.IDE0305.severity = none # IDE0305: Collection initialization can be simplified +dotnet_diagnostic.MSTEST0015.severity = none # MSTEST0015: Test method {method} should not be ignored dotnet_diagnostic.MSTEST0037.severity = error # MSTEST0037: Use proper 'Assert' methods dotnet_diagnostic.SYSLIB1045.severity = none # SYSLIB1045: diagnostics for regex source generation dotnet_naming_rule.abstract_method_should_be_pascal_case.severity = warning diff --git a/Adaptation/.vscode/tasks.json b/Adaptation/.vscode/tasks.json index cefa51f..b0535c9 100644 --- a/Adaptation/.vscode/tasks.json +++ b/Adaptation/.vscode/tasks.json @@ -1,19 +1,134 @@ { "version": "2.0.0", + "inputs": [ + { + "default": "Development", + "description": "Which ASP Net Core Environment?", + "id": "ASPNETCORE_ENVIRONMENT", + "options": [ + "Development", + "Production" + ], + "type": "pickString" + }, + { + "default": "{AssemblyTitle}", + "description": "What Assembly Title?", + "id": "AssemblyTitle", + "type": "promptString" + }, + { + "default": "{Build.BuildId}", + "description": "Which Build BuildId?", + "id": "Build.BuildId", + "type": "promptString" + }, + { + "default": "{Build.Reason}", + "description": "Which Build Reason?", + "id": "Build.Reason", + "type": "promptString" + }, + { + "default": "{Build.Repository.Id}", + "description": "Which Build Repository Id?", + "id": "Build.Repository.Id", + "type": "promptString" + }, + { + "default": "{Build.Repository.Name}", + "description": "Which Build Repository Name?", + "id": "Build.Repository.Name", + "type": "promptString" + }, + { + "default": "{Build.SourceVersion}", + "description": "Which Build Source Version?", + "id": "Build.SourceVersion", + "type": "promptString" + }, + { + "default": "Debug", + "description": "Which Configuration?", + "id": "Configuration", + "options": [ + "Debug", + "Release" + ], + "type": "pickString" + }, + { + "default": "net8.0", + "description": "Which Core Version?", + "id": "CoreVersion", + "options": [ + "net8.0" + ], + "type": "pickString" + }, + { + "default": "C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/MSBuild/Current/Bin/MSBuild.exe", + "description": "Which MS Build?", + "id": "MSBuild", + "type": "promptString" + }, + { + "default": "https://artifactory.intra.infineon.com/artifactory/api/nuget/ngt-fi-package-main-vir/", + "description": "Which Nuget Source?", + "id": "NugetSource", + "type": "promptString" + }, + { + "default": "win-x64", + "description": "Which Runtime?", + "id": "Runtime", + "options": [ + "win-x64", + "win-x32", + "linux-x64", + "linux-x32" + ], + "type": "pickString" + }, + { + "default": "L:/", + "description": "Which System DefaultWorkingDirectory?", + "id": "System.DefaultWorkingDirectory", + "options": [ + "L:/", + "D:/", + "C:/" + ], + "type": "pickString" + }, + { + "default": "v4.8", + "description": "Which Core Target Framework Version?", + "id": "TargetFrameworkVersion", + "options": [ + "v4.8" + ], + "type": "pickString" + }, + { + "default": "{UserSecretsId}", + "description": "Which Core User Secrets Id?", + "id": "UserSecretsId", + "type": "promptString" + } + ], "tasks": [ { "label": "Build", "command": "dotnet", "type": "process", "args": [ - "build", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" + "build" ], "problemMatcher": "$msCompile" }, { - "label": "Test-Debug", + "label": "Test Debug", "command": "dotnet", "type": "process", "args": [ @@ -24,7 +139,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Test-Release", + "label": "Test Release", "command": "dotnet", "type": "process", "args": [ @@ -50,7 +165,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Format-Whitespaces", + "label": "Format Whitespaces", "command": "dotnet", "type": "process", "args": [ @@ -87,13 +202,13 @@ "problemMatcher": "$msCompile" }, { - "label": "Project", + "label": "Code Project", "type": "shell", "command": "code ../MET08THFTIRQS408M.csproj", "problemMatcher": [] }, { - "label": "Readme", + "label": "Code Read Me", "type": "shell", "command": "code ../README.md", "problemMatcher": [] @@ -113,7 +228,7 @@ "problemMatcher": [] }, { - "label": "Git Config", + "label": "Code Git Config", "type": "shell", "command": "code ../.git/config", "problemMatcher": [] diff --git a/Adaptation/FileHandlers/APC/FileRead.cs b/Adaptation/FileHandlers/APC/FileRead.cs index a9ed0b0..a90c002 100644 --- a/Adaptation/FileHandlers/APC/FileRead.cs +++ b/Adaptation/FileHandlers/APC/FileRead.cs @@ -128,7 +128,7 @@ public class FileRead : Shared.FileRead, IFileRead Test[] tests = (from l in descriptions select (Test)l.Test).ToArray(); if (_IsEAFHosted && _FileConnectorConfiguration.FileScanningIntervalInSeconds > 0) FileCopy(reportFullPath, dateTime, descriptions); - results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics[0]), tests, jsonElements, new List()); + results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics), tests, jsonElements, new List()); return results; } diff --git a/Adaptation/FileHandlers/Archive/FileRead.cs b/Adaptation/FileHandlers/Archive/FileRead.cs index 80325c0..5cdb4f2 100644 --- a/Adaptation/FileHandlers/Archive/FileRead.cs +++ b/Adaptation/FileHandlers/Archive/FileRead.cs @@ -153,7 +153,7 @@ public class FileRead : Shared.FileRead, IFileRead Test[] tests = (from l in descriptions select (Test)l.Test).ToArray(); if (_IsEAFHosted && _FileConnectorConfiguration.FileScanningIntervalInSeconds > 0) MoveArchive(reportFullPath, dateTime); - results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics[0]), tests, jsonElements, new List()); + results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics), tests, jsonElements, new List()); return results; } diff --git a/Adaptation/FileHandlers/IQSSi/FileRead.cs b/Adaptation/FileHandlers/IQSSi/FileRead.cs index a60ec91..ebd71ad 100644 --- a/Adaptation/FileHandlers/IQSSi/FileRead.cs +++ b/Adaptation/FileHandlers/IQSSi/FileRead.cs @@ -127,7 +127,7 @@ public class FileRead : Shared.FileRead, IFileRead Test[] tests = (from l in descriptions select (Test)l.Test).ToArray(); if (_IsEAFHosted && _FileConnectorConfiguration.FileScanningIntervalInSeconds > 0) FileCopy(reportFullPath, dateTime, descriptions); - results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics[0]), tests, jsonElements, new List()); + results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics), tests, jsonElements, new List()); return results; } diff --git a/Adaptation/FileHandlers/MoveMatchingFiles/FileRead.cs b/Adaptation/FileHandlers/MoveMatchingFiles/FileRead.cs index d7158a1..e094654 100644 --- a/Adaptation/FileHandlers/MoveMatchingFiles/FileRead.cs +++ b/Adaptation/FileHandlers/MoveMatchingFiles/FileRead.cs @@ -268,7 +268,7 @@ public class FileRead : Shared.FileRead, IFileRead } } - private static ReadOnlyCollection
 GetPreCollection(int numberLength, string parentDirectory, ReadOnlyCollection matchingFiles, bool _)
+    private static ReadOnlyCollection
 GetPreCollection(int numberLength, string parentDirectory, ReadOnlyCollection matchingFiles)
     {
         List
 results = new();
         Pre pre;
@@ -313,8 +313,13 @@ public class FileRead : Shared.FileRead, IFileRead
                 continue;
             if (!_StaticRuns.TryGetValue(_Logistics.Sequence, out List? wsResults))
                 wsResults = null;
-            ProcessDataStandardFormat.Write(preWith.CheckFile, processDataStandardFormat, wsResults);
-            File.Delete(preWith.MatchingFile);
+            if (processDataStandardFormat.InputPDSF is null)
+                File.Move(preWith.MatchingFile, preWith.CheckFile);
+            else
+            {
+                ProcessDataStandardFormat.Write(preWith.CheckFile, processDataStandardFormat, wsResults);
+                File.Delete(preWith.MatchingFile);
+            }
             if (Directory.Exists(preWith.NoWaitDirectory))
             {
                 post = new(preWith.CheckFile, preWith.ErrFile);
@@ -366,10 +371,7 @@ public class FileRead : Shared.FileRead, IFileRead
             { CreatePointerFile(numberLength, parentParentDirectory, matchingFiles); }
             catch (Exception) { }
         }
-        JsonElement[] jsonElements = ProcessDataStandardFormat.GetArray(processDataStandardFormat);
-        List descriptions = QS408M.ProcessData.GetDescriptions(jsonElements);
-        bool mesEntityMatchesProcess = descriptions.Count > 0 && descriptions[0].MesEntity == descriptions[0].Reactor;
-        ReadOnlyCollection
 preCollection = GetPreCollection(numberLength, parentParentDirectory, matchingFiles, mesEntityMatchesProcess);
+        ReadOnlyCollection
 preCollection = GetPreCollection(numberLength, parentParentDirectory, matchingFiles);
         ReadOnlyCollection preWithCollection = GetPreWithCollection(preCollection);
         MoveCollection(dateTime, processDataStandardFormat, preWithCollection);
         return results;
diff --git a/Adaptation/FileHandlers/OpenInsight/FileRead.cs b/Adaptation/FileHandlers/OpenInsight/FileRead.cs
index 27ac683..8f52ea0 100644
--- a/Adaptation/FileHandlers/OpenInsight/FileRead.cs
+++ b/Adaptation/FileHandlers/OpenInsight/FileRead.cs
@@ -113,73 +113,92 @@ public class FileRead : Shared.FileRead, IFileRead
         return results;
     }
 
-    private static string GetLines(List descriptions)
+    private static string GetLines(Logistics logistics, List descriptions, bool isStratusQual)
     {
         StringBuilder results = new();
         char del = '\t';
         QS408M.Description x = descriptions[0];
-        _ = results.Append("Bio-Rad ").Append(x.UniqueId).Append(del).
-            Append(x.Date).Append(del).
-            Append(x.ThicknessFourteenCriticalPointsAverage).Append(del).
-            Append(x.Recipe).Append(del).
-            Append(x.Reactor).Append(del).
-            Append(x.RDS).Append(del).
-            Append(x.PSN).Append(del).
-            Append(x.Layer).Append(del).
-            Append(x.Zone).Append(del).
-            Append(x.Cassette).Append(del).
-            Append(x.Wafer).Append(del).
-            Append(x.RVThickness);
-        for (int i = 0; i < descriptions.Count; i++)
-            _ = results.Append(del).Append(descriptions[i].Position).
-                        Append(del).Append(descriptions[i].Thickness);
+        if (isStratusQual)
+        {
+            _ = results.Append("Stratus_").Append(logistics.MID).Append('_').Append(logistics.DateTimeFromSequence.ToString("yyyyMMddhhmmssfff")).Append(del).
+                Append(x.Date).Append(del).
+                Append(logistics.JobID).Append(del).
+                Append("FQA Thickness").Append(del).
+                Append(x.Employee).Append(del).
+                Append(x.Recipe).Append(del).
+                Append(x.Reactor).Append(del).
+                Append(x.RDS).Append(del).
+                Append(x.PSN).Append(del).
+                Append(x.Lot).Append(del).
+                Append(x.Cassette).Append(del).
+                Append(x.MeanThickness);
+            for (int i = 0; i < descriptions.Count; i++)
+                _ = results.Append(del).Append(descriptions[i].Slot).
+                            Append(del).Append(descriptions[i].Thickness);
+        }
+        else
+        {
+            _ = results.Append("Bio-Rad ").Append(x.UniqueId).Append(del).
+                Append(x.Date).Append(del).
+                Append(x.ThicknessFourteenCriticalPointsAverage).Append(del).
+                Append(x.Recipe).Append(del).
+                Append(x.Reactor).Append(del).
+                Append(x.RDS).Append(del).
+                Append(x.PSN).Append(del).
+                Append(x.Layer).Append(del).
+                Append(x.Zone).Append(del).
+                Append(x.Cassette).Append(del).
+                Append(x.Wafer).Append(del).
+                Append(x.RVThickness);
+            for (int i = 0; i < descriptions.Count; i++)
+                _ = results.Append(del).Append(descriptions[i].Position).
+                            Append(del).Append(descriptions[i].Thickness);
+        }
         return results.ToString();
     }
 
     private void SaveOpenInsightFile(string reportFullPath, DateTime dateTime, ProcessDataStandardFormat processDataStandardFormat, List descriptions, Test[] tests)
     {
+        string duplicateFile;
         bool isDummyRun = false;
         List<(Shared.Properties.IScopeInfo, string)> collection = new();
         string duplicateDirectory = Path.Combine(_FileConnectorConfiguration.SourceFileLocation, _CellInstanceName);
+        bool isStratusQual = _Logistics.MesEntity is "BIORAD4" or "BIORAD5" && _Logistics.MesEntity == _Logistics.ProcessJobID;
+        if (isStratusQual)
+            duplicateDirectory = duplicateDirectory.Replace("MET08THFTIRQS408M", "MET08THFTIRSTRATUS");
         if (!Directory.Exists(duplicateDirectory))
             _ = Directory.CreateDirectory(duplicateDirectory);
         string successDirectory = _FileConnectorConfiguration.AlternateTargetFolder;
-        if (!Directory.Exists(Path.Combine(duplicateDirectory, "1")))
+        if (descriptions.Count == 0 || tests.Length == 0)
+            duplicateFile = Path.Combine(duplicateDirectory, Path.GetFileName(reportFullPath));
+        else
         {
-            string parentParent = GetParentParent(_FileConnectorConfiguration.SourceFileLocation);
-            if (parentParent.Contains(_CellInstanceName))
-                parentParent = Path.GetDirectoryName(parentParent);
-            duplicateDirectory = Path.Combine(parentParent, "Data");
-            if (!Directory.Exists(duplicateDirectory))
-                _ = Directory.CreateDirectory(duplicateDirectory);
-        }
-        string duplicateFile = Path.Combine(duplicateDirectory, Path.GetFileName(reportFullPath));
-        if (descriptions.Count != 0 && tests.Length != 0)
-        {
-            string lines = GetLines(descriptions);
-            if (!string.IsNullOrEmpty(lines))
+            long? subgroupId;
+            string fileName = Path.GetFileName(reportFullPath);
+            long breakAfter = dateTime.AddSeconds(_BreakAfterSeconds).Ticks;
+            long preWait = _FileConnectorConfiguration?.FileHandleWaitTime is null ? dateTime.AddMilliseconds(1234).Ticks : dateTime.AddMilliseconds(_FileConnectorConfiguration.FileHandleWaitTime.Value).Ticks;
+            if (string.IsNullOrEmpty(descriptions[0].Reactor) || string.IsNullOrEmpty(descriptions[0].PSN))
+                subgroupId = null;
+            else
+                (subgroupId, int? _, string _) = FromIQS.GetCommandText(_IqsConnectionString, _Logistics, descriptions[0], breakAfter, preWait);
+            string lines = GetLines(_Logistics, descriptions, isStratusQual);
+            if (subgroupId is null)
+                collection.Add(new(new ScopeInfo(tests[0], _OpenInsightFilePattern), lines));
+            else
+                collection.Add(new(new ScopeInfo(tests[0], $"{subgroupId.Value} {_OpenInsightFilePattern}"), lines));
+            if (_StaticRuns.TryGetValue(_Logistics.Sequence, out List wsResults))
             {
-                long? subgroupId;
-                long breakAfter = dateTime.AddSeconds(_BreakAfterSeconds).Ticks;
-                long preWait = _FileConnectorConfiguration?.FileHandleWaitTime is null ? dateTime.AddMilliseconds(1234).Ticks : dateTime.AddMilliseconds(_FileConnectorConfiguration.FileHandleWaitTime.Value).Ticks;
-                if (string.IsNullOrEmpty(descriptions[0].Reactor) || string.IsNullOrEmpty(descriptions[0].PSN))
-                    subgroupId = null;
-                else
-                    (subgroupId, int? _, string _) = FromIQS.GetCommandText(_IqsConnectionString, _Logistics, descriptions[0], breakAfter, preWait);
-                if (subgroupId is null)
-                    collection.Add(new(new ScopeInfo(tests[0], _OpenInsightFilePattern), lines));
-                else
-                    collection.Add(new(new ScopeInfo(tests[0], $"{subgroupId.Value} {_OpenInsightFilePattern}"), lines));
-                if (_StaticRuns.TryGetValue(_Logistics.Sequence, out List wsResults))
-                {
-                    if (wsResults is null || wsResults.Count != 1)
-                        throw new NullReferenceException($"{nameof(wsResults)} {wsResults?.Count} != 1 {_Logistics.Sequence}!");
-                    lock (_StaticRuns)
-                        wsResults[0] = WS.Results.Get(wsResults[0], subgroupId);
-                }
-                string weekOfYear = _Calendar.GetWeekOfYear(_Logistics.DateTimeFromSequence, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
-                FromIQS.Save(_OpenInsightApiECDirectory, _Logistics, reportFullPath, processDataStandardFormat, descriptions.First(), lines, subgroupId, weekOfYear);
+                if (wsResults is null || wsResults.Count != 1)
+                    throw new NullReferenceException($"{nameof(wsResults)} {wsResults?.Count} != 1 {_Logistics.Sequence}!");
+                lock (_StaticRuns)
+                    wsResults[0] = WS.Results.Get(wsResults[0], subgroupId);
             }
+            if (!fileName.StartsWith("Viewer"))
+                duplicateFile = Path.Combine(duplicateDirectory, $"{subgroupId} {fileName}".TrimStart());
+            else
+                duplicateFile = Path.Combine(duplicateDirectory, $"{$"Viewer {subgroupId}".TrimEnd()} {fileName.Replace("Viewer", string.Empty)}");
+            string weekOfYear = _Calendar.GetWeekOfYear(_Logistics.DateTimeFromSequence, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
+            FromIQS.Save(_OpenInsightApiECDirectory, _Logistics, reportFullPath, processDataStandardFormat, descriptions.First(), subgroupId, weekOfYear);
         }
         if (_IsEAFHosted && _FileConnectorConfiguration.FileScanningIntervalInSeconds > 0)
             WaitForFileConsumption(dateTime, descriptions, isDummyRun, successDirectory, duplicateDirectory, collection, duplicateFile);
diff --git a/Adaptation/FileHandlers/OpenInsight/FromIQS.cs b/Adaptation/FileHandlers/OpenInsight/FromIQS.cs
index e345ad0..dda3113 100644
--- a/Adaptation/FileHandlers/OpenInsight/FromIQS.cs
+++ b/Adaptation/FileHandlers/OpenInsight/FromIQS.cs
@@ -376,7 +376,7 @@ public class FromIQS
         return result;
     }
 
-    internal static void Save(string openInsightApiECDirectory, Logistics logistics, string reportFullPath, ProcessDataStandardFormat processDataStandardFormat, QS408M.Description description, string lines, long? subGroupId, string weekOfYear)
+    internal static void Save(string openInsightApiECDirectory, Logistics logistics, string reportFullPath, ProcessDataStandardFormat processDataStandardFormat, QS408M.Description description, long? subGroupId, string weekOfYear)
     {
         string checkFile;
         string fileName = Path.GetFileName(reportFullPath);
@@ -390,15 +390,9 @@ public class FromIQS
         checkFile = Path.Combine(ecDirectory, fileName);
         if (ecExists && !File.Exists(checkFile))
             File.Copy(reportFullPath, checkFile);
-        checkFile = Path.Combine(ecDirectory, $"{logistics.DateTimeFromSequence.Ticks}.txt");
-        if (ecExists && !File.Exists(checkFile))
-            File.WriteAllText(checkFile, lines);
         checkFile = Path.Combine(ecDirectory, $"{logistics.DateTimeFromSequence.Ticks}.json");
         if (ecExists && !File.Exists(checkFile))
             File.WriteAllText(checkFile, json);
-        checkFile = Path.Combine(ecDirectory, $"{logistics.DateTimeFromSequence.Ticks}.lbl");
-        if (ecExists && !File.Exists(checkFile))
-            File.WriteAllText(checkFile, processDataStandardFormat.Body[processDataStandardFormat.Body.Count - 1]);
     }
 
     private static string GetCommandText(string[] iqsCopyValues)
diff --git a/Adaptation/FileHandlers/OpenInsightMetrologyViewer/FileRead.cs b/Adaptation/FileHandlers/OpenInsightMetrologyViewer/FileRead.cs
index 33bebab..650e6df 100644
--- a/Adaptation/FileHandlers/OpenInsightMetrologyViewer/FileRead.cs
+++ b/Adaptation/FileHandlers/OpenInsightMetrologyViewer/FileRead.cs
@@ -147,7 +147,7 @@ public class FileRead : Shared.FileRead, IFileRead
         Test[] tests = (from l in descriptions select (Test)l.Test).ToArray();
         if (_IsEAFHosted && _FileConnectorConfiguration.FileScanningIntervalInSeconds > 0)
             SendData(reportFullPath, dateTime, descriptions);
-        results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics[0]), tests, jsonElements, new List());
+        results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics), tests, jsonElements, new List());
         return results;
     }
 
diff --git a/Adaptation/FileHandlers/OpenInsightMetrologyViewerAttachments/FileRead.cs b/Adaptation/FileHandlers/OpenInsightMetrologyViewerAttachments/FileRead.cs
index 711b115..63127eb 100644
--- a/Adaptation/FileHandlers/OpenInsightMetrologyViewerAttachments/FileRead.cs
+++ b/Adaptation/FileHandlers/OpenInsightMetrologyViewerAttachments/FileRead.cs
@@ -171,7 +171,7 @@ public class FileRead : Shared.FileRead, IFileRead
         Test[] tests = (from l in descriptions select (Test)l.Test).ToArray();
         if (_IsEAFHosted && _FileConnectorConfiguration.FileScanningIntervalInSeconds > 0)
             PostOpenInsightMetrologyViewerAttachments(descriptions);
-        results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics[0]), tests, jsonElements, new List());
+        results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics), tests, jsonElements, new List());
         return results;
     }
 
diff --git a/Adaptation/FileHandlers/Processed/FileRead.cs b/Adaptation/FileHandlers/Processed/FileRead.cs
index 0edae24..1d9fd26 100644
--- a/Adaptation/FileHandlers/Processed/FileRead.cs
+++ b/Adaptation/FileHandlers/Processed/FileRead.cs
@@ -172,7 +172,7 @@ public class FileRead : Shared.FileRead, IFileRead
         JsonElement[] jsonElements = ProcessDataStandardFormat.GetArray(processDataStandardFormat);
         List descriptions = QS408M.ProcessData.GetDescriptions(jsonElements);
         Test[] tests = (from l in descriptions select (Test)l.Test).ToArray();
-        results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics[0]), tests, jsonElements, new List());
+        results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics), tests, jsonElements, new List());
         if (_IsEAFHosted && _FileConnectorConfiguration.FileScanningIntervalInSeconds > 0)
             DirectoryMove(reportFullPath, dateTime, descriptions);
         else if (!_IsEAFHosted)
diff --git a/Adaptation/FileHandlers/SPaCe/FileRead.cs b/Adaptation/FileHandlers/SPaCe/FileRead.cs
index 2e0f55a..e258bd6 100644
--- a/Adaptation/FileHandlers/SPaCe/FileRead.cs
+++ b/Adaptation/FileHandlers/SPaCe/FileRead.cs
@@ -125,7 +125,7 @@ public class FileRead : Shared.FileRead, IFileRead
         Test[] tests = (from l in descriptions select (Test)l.Test).ToArray();
         if (_IsEAFHosted && _FileConnectorConfiguration.FileScanningIntervalInSeconds > 0)
             FileCopy(reportFullPath, dateTime, descriptions);
-        results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics[0]), tests, jsonElements, new List());
+        results = new Tuple>(string.Join(Environment.NewLine, processDataStandardFormat.Logistics), tests, jsonElements, new List());
         return results;
     }
 
diff --git a/Adaptation/MET08THFTIRQS408M.yml b/Adaptation/MET08THFTIRQS408M.yml
index d9b1abb..c1a90e9 100644
--- a/Adaptation/MET08THFTIRQS408M.yml
+++ b/Adaptation/MET08THFTIRQS408M.yml
@@ -38,7 +38,7 @@ stages:
             displayName: "Echo Check"
 
           - script: '"C:\program files\dotnet\dotnet.exe" nuget locals all --clear'
-            displayName: "Nuget Nuget Clear"
+            displayName: "Nuget Clear"
             enabled: false
 
           - task: CopyFiles@2
@@ -199,7 +199,7 @@ stages:
             displayName: "Echo Check"
 
           - script: '"C:\program files\dotnet\dotnet.exe" nuget locals all --clear'
-            displayName: "Nuget Nuget Clear"
+            displayName: "Nuget Clear"
             enabled: false
 
           - task: CopyFiles@2
diff --git a/Adaptation/Shared/FileRead.cs b/Adaptation/Shared/FileRead.cs
index 49a4526..03b4109 100644
--- a/Adaptation/Shared/FileRead.cs
+++ b/Adaptation/Shared/FileRead.cs
@@ -478,27 +478,14 @@ public class FileRead : Properties.IFileRead
         }
     }
 
-    protected void WritePDSF(IFileRead fileRead, JsonElement[] jsonElements)
+    protected static void WritePDSF(IFileRead fileRead, JsonElement[] jsonElements)
     {
-        string directory;
-        string day = $"{_Logistics.DateTimeFromSequence:yyyy-MM-dd}";
-        string weekOfYear = _Calendar.GetWeekOfYear(_Logistics.DateTimeFromSequence, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
-        string weekDirectory = $"{_Logistics.DateTimeFromSequence:yyyy}_Week_{weekOfYear}";
-        if (!_CellInstanceConnectionName.StartsWith(_CellInstanceName) && _CellInstanceConnectionNameBase == _EquipmentType)
-            directory = Path.Combine(_TracePath, _EquipmentType, "Target", weekDirectory, day, _CellInstanceName, _CellInstanceConnectionName);
-        else
-            directory = Path.Combine(_TracePath, _EquipmentType, "Source", weekDirectory, day, _CellInstanceName, _CellInstanceConnectionName);
-        if (!Directory.Exists(directory))
-            _ = Directory.CreateDirectory(directory);
-        string file = Path.Combine(directory, string.Concat(_Logistics.MesEntity, "_", _Logistics.Sequence, ".ipdsf"));
-        string lines = ProcessDataStandardFormat.GetPDSFText(fileRead, _Logistics, jsonElements, logisticsText: string.Empty);
-        File.WriteAllText(file, lines);
-        if (_Logistics.TotalSecondsSinceLastWriteTimeFromSequence > 600)
-        {
-            try
-            { File.SetLastWriteTime(file, _Logistics.DateTimeFromSequence); }
-            catch (Exception) { }
-        }
+#pragma warning disable CA1510
+        if (fileRead is null)
+            throw new ArgumentNullException(nameof(fileRead));
+        if (jsonElements is null)
+            throw new ArgumentNullException(nameof(jsonElements));
+#pragma warning restore CA1510
     }
 
     protected void WaitForThread(Thread thread, List threadExceptions)
diff --git a/Adaptation/Shared/ProcessDataStandardFormat.cs b/Adaptation/Shared/ProcessDataStandardFormat.cs
index bdcc49e..82237b2 100644
--- a/Adaptation/Shared/ProcessDataStandardFormat.cs
+++ b/Adaptation/Shared/ProcessDataStandardFormat.cs
@@ -398,13 +398,21 @@ internal class ProcessDataStandardFormat
             line = string.Concat(line.Substring(0, line.Length - 1), '}');
             lines.Add(line);
         }
+        string? json = null;
+        if (processDataStandardFormat.Footer is not null && processDataStandardFormat.Footer.Count > 0)
+        {
+            Dictionary footerKeyValuePairs = GetFooterKeyValuePairs(processDataStandardFormat.Footer);
+            Dictionary> logisticKeyValuePairs = GetLogisticKeyValuePairs(processDataStandardFormat.Footer, footerKeyValuePairs);
+            json = JsonSerializer.Serialize(logisticKeyValuePairs, DictionaryStringDictionaryStringStringSourceGenerationContext.Default.DictionaryStringDictionaryStringString);
+        }
+        string footerText = string.IsNullOrEmpty(json) || json == "{}" ? string.Empty : $",{Environment.NewLine}\"PDSF\":{Environment.NewLine}{json}";
         result = string.Concat(
             '{',
             Environment.NewLine,
             '"',
             "Count",
             '"',
-            ": ",            
+            ": ",
             processDataStandardFormat.Body.Count,
             ',',
             Environment.NewLine,
@@ -423,17 +431,95 @@ internal class ProcessDataStandardFormat
             '"',
             "Sequence",
             '"',
-            ": ",            
+            ": ",
             processDataStandardFormat.Sequence,
             Environment.NewLine,
+            footerText,
+            Environment.NewLine,
             '}');
         return result;
-#pragma warning restore CA1845, IDE0057
+    }
+
+    private static Dictionary GetFooterKeyValuePairs(ReadOnlyCollection footerLines)
+    {
+        Dictionary results = new();
+        string[] segments;
+        foreach (string footerLine in footerLines)
+        {
+            segments = footerLine.Split('\t');
+            if (segments.Length != 2 || string.IsNullOrEmpty(segments[1].Trim()))
+            {
+                continue;
+            }
+            if (segments[1].Contains(';'))
+            {
+                continue;
+            }
+            else
+            {
+                if (results.ContainsKey(segments[0]))
+                {
+                    continue;
+                }
+                results.Add(segments[0], segments[1]);
+            }
+        }
+        return results;
+    }
+
+    private static Dictionary> GetLogisticKeyValuePairs(ReadOnlyCollection footerLines, Dictionary footerKeyValuePairs)
+    {
+        Dictionary> results = new();
+        string[] segments;
+        string[] subSegments;
+        string[] subSubSegments;
+        Dictionary? keyValue;
+        results.Add("Footer", footerKeyValuePairs);
+        foreach (string footerLine in footerLines)
+        {
+            segments = footerLine.Split('\t');
+            if (segments.Length != 2 || string.IsNullOrEmpty(segments[1].Trim()))
+            {
+                continue;
+            }
+            if (!segments[1].Contains(';') || !segments[1].Contains('='))
+            {
+                continue;
+            }
+            else
+            {
+                subSegments = segments[1].Split(';');
+                if (subSegments.Length < 1)
+                {
+                    continue;
+                }
+                if (!results.TryGetValue(segments[0], out keyValue))
+                {
+                    results.Add(segments[0], new());
+                    if (!results.TryGetValue(segments[0], out keyValue))
+                    {
+                        throw new Exception();
+                    }
+                }
+                foreach (string segment in subSegments)
+                {
+                    subSubSegments = segment.Split('=');
+                    if (subSubSegments.Length != 2)
+                    {
+                        continue;
+                    }
+                    keyValue.Add(subSubSegments[0], subSubSegments[1]);
+                }
+            }
+        }
+        return results;
     }
 
     internal static void Write(string path, ProcessDataStandardFormat processDataStandardFormat, List? wsResults)
     {
         List results = new();
+        if (processDataStandardFormat.InputPDSF is null)
+            throw new NullReferenceException(nameof(processDataStandardFormat.InputPDSF));
         if (processDataStandardFormat.Sequence is null)
             throw new NullReferenceException(nameof(processDataStandardFormat.Sequence));
         string endOffset = "E#######T";
@@ -471,25 +557,22 @@ internal class ProcessDataStandardFormat
             }
         }
         results.Add("END_HEADER");
-        if (processDataStandardFormat.InputPDSF is not null)
-        {
-            results.Add(string.Empty);
-            List hyphens = new();
-            results.AddRange(processDataStandardFormat.InputPDSF.Header.Select(l => l.Replace('\t', '|')));
-            results.Add(string.Empty);
-            results.Add($"|{string.Join("|", processDataStandardFormat.InputPDSF.Columns)}|");
-            for (int i = 0; i < processDataStandardFormat.InputPDSF.Columns.Count; i++)
-                hyphens.Add('-');
-            results.Add($"|{string.Join("|", hyphens)}|");
-            results.AddRange(processDataStandardFormat.InputPDSF.Body.Select(l => l.Replace('\t', '|')));
-            results.Add(string.Empty);
-            results.AddRange(processDataStandardFormat.InputPDSF.Footer.Select(l => l.Replace('\t', '|')));
-            results.Add(string.Empty);
-            results.Add("EOF");
-            results.Add(string.Empty);
-            string json = GetJson(processDataStandardFormat);
-            results.Add(json);
-        }
+        results.Add(string.Empty);
+        List hyphens = new();
+        results.AddRange(processDataStandardFormat.InputPDSF.Header.Select(l => $"|{l.Replace('\t', '|')}|"));
+        results.Add(string.Empty);
+        results.Add($"|{string.Join("|", processDataStandardFormat.InputPDSF.Columns)}|");
+        for (int i = 0; i < processDataStandardFormat.InputPDSF.Columns.Count; i++)
+            hyphens.Add('-');
+        results.Add($"|{string.Join("|", hyphens)}|");
+        results.AddRange(processDataStandardFormat.InputPDSF.Body.Select(l => $"|{l.Replace('\t', '|')}|"));
+        results.Add(string.Empty);
+        results.AddRange(processDataStandardFormat.InputPDSF.Footer.Select(l => $"|{l.Replace('\t', '|')}|"));
+        results.Add(string.Empty);
+        results.Add("EOF");
+        results.Add(string.Empty);
+        string json = GetJson(processDataStandardFormat);
+        results.Add(json);
         File.WriteAllText(path, string.Join(Environment.NewLine, results));
     }
 
@@ -777,4 +860,10 @@ internal class ProcessDataStandardFormat
 [JsonSerializable(typeof(JsonElement[]))]
 internal partial class JsonElementCollectionSourceGenerationContext : JsonSerializerContext
 {
+}
+
+[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
+[JsonSerializable(typeof(Dictionary>))]
+internal partial class DictionaryStringDictionaryStringStringSourceGenerationContext : JsonSerializerContext
+{
 }
\ No newline at end of file