ソースを参照

Update settings from old versions into new versons.
Add more logging.
Accept jobs with the date after their name in file path.
Handle reconciliation of jobs.

Gregory 3 年 前
コミット
397174bfd5

+ 2 - 2
crusherScanner.sln

@@ -15,8 +15,8 @@ Global
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 		{594D7995-6E4A-44E5-AC36-4E3F544EFF58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{594D7995-6E4A-44E5-AC36-4E3F544EFF58}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{594D7995-6E4A-44E5-AC36-4E3F544EFF58}.Debug|x64.ActiveCfg = Release|x64
-		{594D7995-6E4A-44E5-AC36-4E3F544EFF58}.Debug|x64.Build.0 = Release|x64
+		{594D7995-6E4A-44E5-AC36-4E3F544EFF58}.Debug|x64.ActiveCfg = Debug|x64
+		{594D7995-6E4A-44E5-AC36-4E3F544EFF58}.Debug|x64.Build.0 = Debug|x64
 		{594D7995-6E4A-44E5-AC36-4E3F544EFF58}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{594D7995-6E4A-44E5-AC36-4E3F544EFF58}.Release|Any CPU.Build.0 = Release|Any CPU
 		{594D7995-6E4A-44E5-AC36-4E3F544EFF58}.Release|x64.ActiveCfg = Release|x64

+ 114 - 0
crusherScanner/.editorconfig

@@ -0,0 +1,114 @@
+# Rules in this file were initially inferred by Visual Studio IntelliCode from the C:\Users\gregory\Source\Repos\crusherScanner\crusherScanner\ codebase based on best match to current usage at 25/08/2022
+# You can modify the rules from these initially generated values to suit your own policies
+# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
+[*.cs]
+
+
+#Core editorconfig formatting - indentation
+
+#use soft tabs (spaces) for indentation
+indent_style = space
+
+#Formatting - indentation options
+
+#indent switch case contents.
+csharp_indent_case_contents = true
+#indent switch labels
+csharp_indent_switch_labels = true
+
+#Formatting - new line options
+
+#place catch statements on a new line
+csharp_new_line_before_catch = true
+#place else statements on a new line
+csharp_new_line_before_else = true
+#require finally statements to be on a new line after the closing brace
+csharp_new_line_before_finally = true
+#require members of object initializers to be on the same line
+csharp_new_line_before_members_in_object_initializers = false
+#require braces to be on a new line for object_collection_array_initializers, methods, control_blocks, and types (also known as "Allman" style)
+csharp_new_line_before_open_brace = object_collection_array_initializers, methods, control_blocks, types
+
+#Formatting - organize using options
+
+#sort System.* using directives alphabetically, and place them before other usings
+dotnet_sort_system_directives_first = true
+
+#Formatting - spacing options
+
+#require NO space between a cast and the value
+csharp_space_after_cast = false
+#require a space before the colon for bases or interfaces in a type declaration
+csharp_space_after_colon_in_inheritance_clause = true
+#require a space after a keyword in a control flow statement such as a for loop
+csharp_space_after_keywords_in_control_flow_statements = true
+#require a space before the colon for bases or interfaces in a type declaration
+csharp_space_before_colon_in_inheritance_clause = true
+#remove space within empty argument list parentheses
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+#remove space between method call name and opening parenthesis
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call
+csharp_space_between_method_call_parameter_list_parentheses = false
+#remove space within empty parameter list parentheses for a method declaration
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list.
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+
+#Formatting - wrapping options
+
+#leave code block on single line
+csharp_preserve_single_line_blocks = true
+
+#Style - Code block preferences
+
+#prefer curly braces even for one line of code
+csharp_prefer_braces = true:suggestion
+
+#Style - expression bodied member options
+
+#prefer block bodies for constructors
+csharp_style_expression_bodied_constructors = false:suggestion
+#prefer block bodies for methods
+csharp_style_expression_bodied_methods = false:suggestion
+
+#Style - expression level options
+
+#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
+dotnet_style_predefined_type_for_member_access = true:suggestion
+
+#Style - Expression-level  preferences
+
+#prefer objects to be initialized using object initializers when possible
+dotnet_style_object_initializer = true:suggestion
+#prefer inferred tuple element names
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+
+#Style - implicit and explicit types
+
+#prefer explicit type over var in all cases, unless overridden by another code style rule
+csharp_style_var_elsewhere = false:suggestion
+#prefer explicit type over var to declare variables with built-in system types such as int
+csharp_style_var_for_built_in_types = false:suggestion
+#prefer var when the type is already mentioned on the right-hand side of a declaration expression
+csharp_style_var_when_type_is_apparent = true:suggestion
+
+#Style - language keyword and framework type options
+
+#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+
+#Style - modifier options
+
+#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods.
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
+
+#Style - Modifier preferences
+
+#when this rule is set to a list of modifiers, prefer the specified ordering.
+csharp_preferred_modifier_order = public,private,internal,static,sealed,readonly:suggestion
+
+#Style - qualification options
+
+#prefer fields not to be prefaced with this. or Me. in Visual Basic
+dotnet_style_qualification_for_field = false:suggestion

+ 2 - 1
crusherScanner/CustomTypes.cs

@@ -18,11 +18,12 @@
         public bool isInLims; // Does this sample exist in LIMS.
         public string message; // Reply message to the user.
         public bool messageState; // Is the reply message good or an error.
-        public int BufferCount; // Count of how many samples in memory cache.
+        public int BufferCount; // Count of how many samples in memory cache (Ore Def buffer).
         public bool Contaminated; // Does this sample contain Contaminates.
         public bool SampleNotFound; // Unable to find this sample in job files.
         public int JobTotal; // Total number of samples in the job.
         public int JobCount; // Number of samples scanned in the job.
+        public bool Ins; // Mark this sample as INS.
         public Samples sampleData; // Sample details ie. contaminates, job, etc.
     }
 

+ 3 - 2
crusherScanner/FileAccess.cs

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
 namespace crusherScanner
 {
     /// <summary>
-    /// File access for ML jobs usinf CSV parse.
+    /// File access for ML jobs using CSV parse.
     /// </summary>
     internal class FileAccess
     {
@@ -22,7 +22,8 @@ namespace crusherScanner
                 {
                     if (Directory.Exists(DataDir))
                     {
-                        var jobFiles = Directory.EnumerateFiles(DataDir, "ML??????.csv", SearchOption.TopDirectoryOnly);
+                        // Pattern match is for 'ML003092.csv' or 'ML003092_20220822.csv'
+                        var jobFiles = Directory.EnumerateFiles(DataDir, "ML??????*.csv", SearchOption.TopDirectoryOnly);
                         foreach (var jobFile in jobFiles)
                         {
                             bool sampleFound;

+ 35 - 7
crusherScanner/Form1.Designer.cs

@@ -36,6 +36,7 @@
             this.TextBox1 = new System.Windows.Forms.TextBox();
             this.menuStrip1 = new System.Windows.Forms.MenuStrip();
             this.menuToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.reconciliationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
@@ -75,6 +76,8 @@
             this.label8 = new System.Windows.Forms.Label();
             this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
             this.notifyIcon1 = new System.Windows.Forms.NotifyIcon(this.components);
+            this.backgroundWorker2 = new System.ComponentModel.BackgroundWorker();
+            this.flagINSSampleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.menuStrip1.SuspendLayout();
             this.statusStrip1.SuspendLayout();
             this.SuspendLayout();
@@ -99,10 +102,13 @@
             this.menuStrip1.Size = new System.Drawing.Size(1708, 33);
             this.menuStrip1.TabIndex = 1;
             this.menuStrip1.Text = "menuStrip1";
+            this.menuStrip1.ItemClicked += new System.Windows.Forms.ToolStripItemClickedEventHandler(this.menuStrip1_ItemClicked);
             // 
             // menuToolStripMenuItem
             // 
             this.menuToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.reconciliationToolStripMenuItem,
+            this.flagINSSampleToolStripMenuItem,
             this.settingsToolStripMenuItem,
             this.toolStripSeparator2,
             this.logsToolStripMenuItem,
@@ -114,6 +120,12 @@
             this.menuToolStripMenuItem.Size = new System.Drawing.Size(73, 29);
             this.menuToolStripMenuItem.Text = "Menu";
             // 
+            // reconciliationToolStripMenuItem
+            // 
+            this.reconciliationToolStripMenuItem.Name = "reconciliationToolStripMenuItem";
+            this.reconciliationToolStripMenuItem.Size = new System.Drawing.Size(270, 34);
+            this.reconciliationToolStripMenuItem.Text = "Reconciliation";
+            // 
             // settingsToolStripMenuItem
             // 
             this.settingsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
@@ -124,7 +136,7 @@
             this.toolStripSeparator5,
             this.purgeToolStripMenuItem1});
             this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem";
-            this.settingsToolStripMenuItem.Size = new System.Drawing.Size(195, 34);
+            this.settingsToolStripMenuItem.Size = new System.Drawing.Size(270, 34);
             this.settingsToolStripMenuItem.Text = "Settings";
             // 
             // editToolStripMenuItem
@@ -168,7 +180,7 @@
             // toolStripSeparator2
             // 
             this.toolStripSeparator2.Name = "toolStripSeparator2";
-            this.toolStripSeparator2.Size = new System.Drawing.Size(192, 6);
+            this.toolStripSeparator2.Size = new System.Drawing.Size(267, 6);
             // 
             // logsToolStripMenuItem
             // 
@@ -177,7 +189,7 @@
             this.openDirectoryToolStripMenuItem,
             this.purgeToolStripMenuItem});
             this.logsToolStripMenuItem.Name = "logsToolStripMenuItem";
-            this.logsToolStripMenuItem.Size = new System.Drawing.Size(195, 34);
+            this.logsToolStripMenuItem.Size = new System.Drawing.Size(270, 34);
             this.logsToolStripMenuItem.Text = "Logs";
             // 
             // openCurrentToolStripMenuItem
@@ -204,7 +216,7 @@
             // toolStripSeparator3
             // 
             this.toolStripSeparator3.Name = "toolStripSeparator3";
-            this.toolStripSeparator3.Size = new System.Drawing.Size(192, 6);
+            this.toolStripSeparator3.Size = new System.Drawing.Size(267, 6);
             // 
             // reinitializeToolStripMenuItem
             // 
@@ -212,7 +224,7 @@
             this.reinitializeSettingsToolStripMenuItem,
             this.testSettingsToolStripMenuItem});
             this.reinitializeToolStripMenuItem.Name = "reinitializeToolStripMenuItem";
-            this.reinitializeToolStripMenuItem.Size = new System.Drawing.Size(195, 34);
+            this.reinitializeToolStripMenuItem.Size = new System.Drawing.Size(270, 34);
             this.reinitializeToolStripMenuItem.Text = "Reinitialize";
             // 
             // reinitializeSettingsToolStripMenuItem
@@ -231,12 +243,12 @@
             // toolStripSeparator4
             // 
             this.toolStripSeparator4.Name = "toolStripSeparator4";
-            this.toolStripSeparator4.Size = new System.Drawing.Size(192, 6);
+            this.toolStripSeparator4.Size = new System.Drawing.Size(267, 6);
             // 
             // ExitToolStripMenuItem
             // 
             this.ExitToolStripMenuItem.Name = "ExitToolStripMenuItem";
-            this.ExitToolStripMenuItem.Size = new System.Drawing.Size(195, 34);
+            this.ExitToolStripMenuItem.Size = new System.Drawing.Size(270, 34);
             this.ExitToolStripMenuItem.Text = "Exit";
             this.ExitToolStripMenuItem.Click += new System.EventHandler(this.ExitToolStripMenuItem_Click);
             // 
@@ -408,6 +420,18 @@
             this.notifyIcon1.Visible = true;
             this.notifyIcon1.DoubleClick += new System.EventHandler(this.notifyIcon1_DoubleClick);
             // 
+            // backgroundWorker2
+            // 
+            this.backgroundWorker2.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker2_DoWork);
+            this.backgroundWorker2.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker2_RunWorkerCompleted);
+            // 
+            // flagINSSampleToolStripMenuItem
+            // 
+            this.flagINSSampleToolStripMenuItem.Name = "flagINSSampleToolStripMenuItem";
+            this.flagINSSampleToolStripMenuItem.Size = new System.Drawing.Size(270, 34);
+            this.flagINSSampleToolStripMenuItem.Text = "Flag \'INS\' sample";
+            this.flagINSSampleToolStripMenuItem.Click += new System.EventHandler(this.flagINSSampleToolStripMenuItem_Click);
+            // 
             // Form1
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 25F);
@@ -482,5 +506,9 @@
         private ToolStripStatusLabel toolStripStatusLabel4;
         private ToolStripStatusLabel toolStripStatusLabel5;
         private NotifyIcon notifyIcon1;
+        private System.ComponentModel.BackgroundWorker backgroundWorker2;
+        private ToolStripMenuItem toolStripMenuItem1;
+        private ToolStripMenuItem reconciliationToolStripMenuItem;
+        private ToolStripMenuItem flagINSSampleToolStripMenuItem;
     }
 }

+ 93 - 4
crusherScanner/Form1.cs

@@ -2,8 +2,10 @@ namespace crusherScanner
 {
     public partial class Form1 : Form
     {
+        private char PathSeparator = '\\';
         private static ProgramFunctions? dataCore;
         private static int counter = 0;
+        private bool flagINS = false;
 
         /// <summary>
         /// Form 1 Initializer.
@@ -33,19 +35,27 @@ namespace crusherScanner
             TextBox1.Focus();
             Logging.Append(LogLevel.Information,"Crusher Scanner Application " + Application.ProductVersion + " has started.");
             notifyIcon1.ShowBalloonTip(30, "Crusher Scanner Application", "Crusher Scanner Application, version " + Application.ProductVersion + " has started.", System.Windows.Forms.ToolTipIcon.Info);
+            backgroundWorker2.RunWorkerAsync();
         }
 
         private void TextBox1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
-        {
+        {// test val RCA1275143
             Scan scanned = new();
             scanned.BufferCount = -1;
             counter = 2;
             scanned.barcode = TextBox1.Text.Trim().ToUpper();
             if (e.KeyValue == 13 && dataCore != null)
             {
+                Logging.Append(LogLevel.Information, "Barcode " + scanned.barcode + " has been scanned.");
+                if (flagINS)
+                {
+                    scanned.Ins = true;
+                    flagINS = false;
+                }
                 scanned = ProgramFunctions.StartChecks(scanned);
                 if(scanned.sampleData.Job != null)
                 {
+                    TextBox1.Enabled = false;
                     backgroundWorker1.RunWorkerAsync(scanned);
                 }
                 TextBox1.Text = "";
@@ -65,9 +75,10 @@ namespace crusherScanner
                     label1.Hide();
                 }
 
-                if (scanned.sampleData.Job != null)
+                if (scanned.sampleData.Job != null && label8.Text != $"Job in progress : {scanned.sampleData.Job}")
                 {
                     label8.Text = $"Job in progress : {scanned.sampleData.Job}";
+                    Logging.Append(LogLevel.Information, $"Job in progress : {scanned.sampleData.Job}");
                 }
                 else
                 {
@@ -77,7 +88,10 @@ namespace crusherScanner
                 if (scanned.BufferCount != -1)
                 {
                     toolStripStatusLabel2.Text = scanned.BufferCount.ToString();
+                    Logging.Append(LogLevel.Debug, $"{scanned.BufferCount} samples in the Ore Def buffer.");
                 }
+
+                backgroundWorker2.RunWorkerAsync();
             }
         }
 
@@ -87,7 +101,8 @@ namespace crusherScanner
             {
                 rtLabel.Text = $"Sample {bCode} contains {name}";
                 BackColor = bColor;
-                rtLabel.Show();   
+                rtLabel.Show();
+                Logging.Append(LogLevel.Information, bCode + " contains " + name);
             }
             else
             {
@@ -200,7 +215,6 @@ namespace crusherScanner
         {
             if (e.Argument!=null)
             {
-                TextBox1.Enabled = false;
                 WorkingDirControl.UpdateWorkingDir(e);
             }
         }
@@ -216,6 +230,7 @@ namespace crusherScanner
                 toolStripStatusLabel5.Text = (scan.JobTotal - (int)scan.JobCount).ToString();
             }
             TextBox1.Enabled = true;
+            TextBox1.Focus();
         }
 
         private void openDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
@@ -248,5 +263,79 @@ namespace crusherScanner
             }
             
         }
+
+        private void backgroundWorker2_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
+        {
+            Reconciliation.CheckJobs(e);
+        }
+
+        private void backgroundWorker2_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
+        {
+            if (e.Result != null)
+            {
+                var data = (HashSet<string>)e.Result;
+                if(data.Count>=1)
+                {
+                    foreach (string job in data)
+                    {
+                        string[] temp = job.Split(PathSeparator);
+                        ToolStripMenuItem reconList = new ToolStripMenuItem();
+                        reconList.Text = temp[temp.Length - 1];
+                        reconList.Click += new System.EventHandler(this.FileMenuItemClick);
+                        reconciliationToolStripMenuItem.DropDownItems.Add(reconList);
+                    }
+                }
+            }
+        }
+        
+        private void FileMenuItemClick(object? sender, EventArgs e)
+        {
+            string path = Properties.Settings.Default.OreDefWorkFile;
+            var missingSamples = new HashSet<string>();
+            if (sender!=null)
+            {
+                missingSamples = Reconciliation.CheckJobReconcilableDiscrepencies($"{path}\\{sender}");
+                if (missingSamples.Count >= 1)
+                {
+                    int widthCounter = 0;
+                    string messageReply = $"The following {missingSamples.Count} sample(s) are missing from the job.\n Are you sure you want to reconcile anyway?\n";
+                    foreach (string sample in missingSamples)
+                    {
+                        if (missingSamples.Count >= 31)
+                        {
+                            if ((missingSamples.Count/31) == widthCounter)
+                            {
+                                messageReply += $"{sample}\n";
+                                widthCounter = 0;
+                            }
+                            else
+                            {
+                                messageReply += $"{sample}, ";
+                                widthCounter++;
+                            }
+                        }
+                        else
+                        {
+                            messageReply += $"{sample}\n";
+                        }
+                    }
+                    DialogResult result = MessageBox.Show(messageReply, "Missing Samples",MessageBoxButtons.YesNo, MessageBoxIcon.Question);
+                    if (result == DialogResult.Yes)
+                    {
+                        backgroundWorker2.RunWorkerAsync(sender.ToString());
+                    }
+                }
+            }
+        }
+
+        private void menuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
+        {
+
+        }
+
+        private void flagINSSampleToolStripMenuItem_Click(object sender, EventArgs e)
+        {
+            flagINS = true;
+        }
     }
 }

+ 3 - 0
crusherScanner/Form1.resx

@@ -99,6 +99,9 @@
         YGR5KTMVNgLf5fPVEiEEu5pbJrZvi+R//y05MjeTPjM5OclN08R/s4eJCdHODUcAAAAASUVORK5CYII=
 </value>
   </data>
+  <metadata name="backgroundWorker2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>1229, 17</value>
+  </metadata>
   <data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
     <value>
         AAABAAEAEBAAAAAAIACpAwAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAPAAAAEAgGAAAAyVYlBAAAA3BJ

+ 4 - 2
crusherScanner/LimsAccess.cs

@@ -33,7 +33,9 @@ namespace crusherScanner
             {
 
                 //TODO: Actully check LIMS for sample ID. And fill buffer.
-                barcode.isInLims = true;
+                Logging.Append(LogLevel.Information, $"Unable to contact the LIMS database : {barcode.barcode}");
+                MessageBox.Show($"Unable to contact the LIMS database.", "Crusher Input");
+                barcode.isInLims = false;
                 return barcode;
                 //https://www.guru99.com/c-sharp-access-database.html#:~:text=Code%20Explanation%3A-%201%20The%20first%20step%20is%20to,connection%20to%20the%20database.%20...%20More%20items...%20
                 //string connetionString;
@@ -48,7 +50,7 @@ namespace crusherScanner
             }
             else // LIMS connection disabled. Check for a fall back file or return true.
             {
-                if (CheckFileStore("sampleList.csv") && CheckFileStore(Properties.Settings.Default.OreDefWorkFile + "\\sampleList.csv") && CheckBuffer(barcode))
+                if ((CheckFileStore("sampleList.csv") && CheckFileStore(Properties.Settings.Default.OreDefWorkFile + "\\sampleList.csv")) || CheckBuffer(barcode))
                 {
                     barcode.isInLims = true;
                     return barcode;

+ 16 - 11
crusherScanner/ProgramFunctions.cs

@@ -27,18 +27,23 @@ namespace crusherScanner
             // no initial configuration present
             if (Properties.Settings.Default.OreDefInFile == "")
             {
-                if (File.Exists("config.json"))
+                // Try to get settings from previous version of the app.
+                Properties.Settings.Default.Upgrade();
+                if (Properties.Settings.Default.OreDefInFile == "")
                 {
-                    MessageBox.Show("No configuration stored,\nloading configuration from 'config.json'","Settings",MessageBoxButtons.OK,MessageBoxIcon.Exclamation);
-                    JsonHandler conf = new("config.json", true);
-                    ImportConfigFile("config.json");
-                }
-                else
-                {
-                    MessageBox.Show("No configuration stored,\nYou will need to enter settings manually.\nThe settings will open now.", "Settings", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
-                    SettingsDialog dlg = new();
-                    dlg.ShowDialog();
-                    dlg.Dispose();
+                    if (File.Exists("config.json"))
+                    {
+                        MessageBox.Show("No configuration stored,\nloading configuration from 'config.json'", "Settings", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+                        JsonHandler conf = new("config.json", true);
+                        ImportConfigFile("config.json");
+                    }
+                    else
+                    {
+                        MessageBox.Show("No configuration stored,\nYou will need to enter settings manually.\nThe settings will open now.", "Settings", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+                        SettingsDialog dlg = new();
+                        dlg.ShowDialog();
+                        dlg.Dispose();
+                    }
                 }
             }
             //TODO check if settings are ok to use before setting ready.

+ 121 - 0
crusherScanner/Reconciliation.cs

@@ -0,0 +1,121 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace crusherScanner
+{
+    internal class Reconciliation
+    {
+        /// <summary>
+        /// Get a list of jobs in the working dir that may be reconcilable
+        /// </summary>
+        public static void CheckJobs(System.ComponentModel.DoWorkEventArgs e)
+        {
+            if (e.Argument!=null)
+            {                
+                ForceReconciliation((string)e.Argument);
+            }
+            var jobs = new HashSet<string>();
+            foreach (string jobPath in SupportFunctions.GetFilesFromPath(Properties.Settings.Default.OreDefWorkFile, "ML??????*.csv"))
+            {
+                if(!CheckJobReconcilable(jobPath))
+                {
+                    jobs.Add(jobPath);
+                }
+                else
+                {
+                    string[] job = jobPath.Split('\\');
+                    ForceReconciliation(job[job.Length-1]);
+                }
+            }
+            e.Result = jobs;
+            PruneBackupJobFiles();
+        }
+
+        /// <summary>
+        /// Remove old reconciled jobs once they are older than 24 hours.
+        /// </summary>
+        public static void PruneBackupJobFiles()
+        {
+            //Remove Recond_ML??????.csv once they are 24 hr old.
+            foreach (string jobPath in SupportFunctions.GetFilesFromPath(Properties.Settings.Default.OreDefWorkFile, "Recond_ML??????*.csv"))
+            {
+                DateTime lastMoved = DateTime.MinValue;
+                lastMoved = File.GetLastWriteTime(jobPath);
+                if (lastMoved.AddHours(24) < DateTime.Now)
+                {
+                    File.Delete(jobPath);
+                }
+            }
+
+        }
+
+        /// <summary>
+        /// Copy job file from working to output and make a backup (temporary 24hr)
+        /// </summary>
+        /// <param name="job">Jobfile</param>
+        public static void ForceReconciliation(string job)
+        {
+            string JobPath = Properties.Settings.Default.OreDefWorkFile;
+            string ReconPath = Properties.Settings.Default.OreDefOutFile;
+            
+            if (File.Exists($"{JobPath}\\{job}"))
+            {
+                File.Copy($"{JobPath}\\{job}", $"{JobPath}\\Recond_{job}");
+                File.Move($"{JobPath}\\{job}", $"{ReconPath}\\{job}");
+                File.SetLastWriteTime($"{JobPath}\\Recond_{job}", DateTime.Now);
+            }
+        }
+
+        /// <summary>
+        /// Check if given job can be reconciled automatically
+        /// </summary>
+        /// <param name="jobPath">Full path to job csv file</param>
+        /// <returns>True if could be reconciled</returns>
+        private static bool CheckJobReconcilable(string jobPath)
+        {
+            string[] lines = File.ReadAllLines(jobPath);
+
+            foreach (string line in lines)
+            {
+                if (line.Contains(','))
+                {
+                    string[] RawSampleData = line.Split(',');
+                    if (RawSampleData.Length == 9)
+                    {
+                        if(RawSampleData[0] != "SAMPLEID" && RawSampleData[0] != RawSampleData[8])
+                        {
+                            return false;
+                        }
+                    }
+                }
+            }
+            return true;
+        }
+
+
+        public static HashSet<string> CheckJobReconcilableDiscrepencies(string jobPath)
+        {
+            string[] lines = File.ReadAllLines(jobPath);
+            HashSet<string> discrepencies = new HashSet<string>();
+
+            foreach (string line in lines)
+            {
+                if (line.Contains(','))
+                {
+                    string[] RawSampleData = line.Split(',');
+                    if (RawSampleData.Length == 9)
+                    {
+                        if (RawSampleData[0] != "SAMPLEID" && RawSampleData[0] != RawSampleData[8] && RawSampleData[8] != "INS")
+                        {
+                            discrepencies.Add(RawSampleData[0]);
+                        }
+                    }
+                }
+            }
+            return discrepencies;
+        }
+    }
+}

+ 0 - 1
crusherScanner/SerialAccess.cs

@@ -38,7 +38,6 @@ namespace crusherScanner
         /// </summary>
         private static void SetupSerial()
         {
-            //TODO: use settings for connection
             _serialPort = SetSerialSettings();
 
             try

+ 47 - 0
crusherScanner/SupportFunctions.cs

@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace crusherScanner
+{
+    internal static class SupportFunctions
+    {
+        /// <summary>
+        /// Return a hash set of files in given folder.
+        /// <code>
+        /// GetFilesFromPath("c:\oreDefFiles\","ML??????*.csv");
+        /// </code>
+        /// </summary>
+        /// <param name="Path">Path to search for files at.</param>
+        /// <param name="Mask">Mask to check files against.</param>
+        /// <returns>HashSet/<string/> of files in folder.</returns>
+        public static HashSet<string> GetFilesFromPath(string Path,string Mask)
+        {
+            HashSet<string> files = new HashSet<string>();
+            try
+            {
+                if (Directory.Exists(Path))
+                {
+                    // Pattern match is for 'ML003092.csv' or 'ML003092_20220822.csv' = "ML??????*.csv"
+                    IEnumerable<string> jobFiles = Directory.EnumerateFiles(Path, Mask, SearchOption.TopDirectoryOnly);
+                    foreach (string jobFile in jobFiles)
+                    {
+                        files.Add(jobFile);
+                    }
+                }
+                else
+                {
+                    Logging.Append(LogLevel.Debug, $"Unable to get files from non existant 'Path' {Path}.");
+                }
+            }
+            catch (Exception ex)
+            {
+                Logging.Append(LogLevel.Error ,"An error occurred." + ex.Message);
+            }                                                                                                                                                     
+            // return an empty HashSet if no files found
+            return files;
+        }
+    }
+}

+ 11 - 3
crusherScanner/WorkingDirControl.cs

@@ -31,7 +31,7 @@ namespace crusherScanner
                 {
                     try
                     {
-                        File.Move($"{DataDirs[1]}\\{scanData.sampleData.Job}.csv", $"{DataDirs[0]}\\{scanData.sampleData.Job}.csv");
+                        File.Move(JobFile, $"{DataDirs[0]}\\{scanData.sampleData.Job}.csv");
                         scanData = updateSampleInJobFile(scanData, $"{DataDirs[0]}\\{scanData.sampleData.Job}.csv");
                     }
                     catch (Exception)
@@ -60,7 +60,8 @@ namespace crusherScanner
                 {
                     if (Directory.Exists(DataDir))
                     {
-                        var jobFiles = Directory.EnumerateFiles(DataDir, "ML??????.csv", SearchOption.TopDirectoryOnly);
+                        // Pattern match is for 'ML003092.csv' or 'ML003092_20220822.csv'
+                        var jobFiles = Directory.EnumerateFiles(DataDir, "ML??????*.csv", SearchOption.TopDirectoryOnly);
                         foreach (var jobFile in jobFiles)
                         {
                             if (jobFile.Contains(scanData.sampleData.Job))
@@ -94,7 +95,14 @@ namespace crusherScanner
                             string[] CSV = line.Split('\u002C');
                             if(CSV[0] == scanData.sampleData.Sid)
                             {
-                                CSV[8] = scanData.sampleData.Sid;
+                                if (scanData.Ins)
+                                {
+                                    CSV[8] = "INS";
+                                }
+                                else
+                                {
+                                    CSV[8] = scanData.sampleData.Sid;
+                                }
                                 fileLines.Add(string.Join('\u002C', CSV));
                             }
                             else

+ 2 - 3
crusherScanner/crusherScanner.csproj

@@ -16,9 +16,8 @@
     <DelaySign>False</DelaySign>
     <SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion>
     <Platforms>AnyCPU;x64</Platforms>
-    <AssemblyVersion>1.0.*</AssemblyVersion>
-    <FileVersion>1.0.*</FileVersion>
-	<Deterministic>false</Deterministic>
+    <AssemblyVersion>1.0.2</AssemblyVersion>
+    <FileVersion>1.0.2</FileVersion>
 	<NeutralLanguage>en-AU</NeutralLanguage>
   </PropertyGroup>