Sfoglia il codice sorgente

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 anni fa
parent
commit
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>