/// <summary>
        /// This method is called when the user clicks on the browse button for a keyence file. It makes sure everything is valid as well
        /// (through calls to backend methods).
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BTN_BROWSE_KEYENCE_CLICK(object sender, EventArgs e) //Logged and documented.
        {
            EVENTS.LOG_MESSAGE(1, "ENTER");
            OpenFileDialog FILE_PATH_BROWSER = new OpenFileDialog();

            //Set properties of the open file dialog.
            FILE_PATH_BROWSER.InitialDirectory = Properties.Misc.Default.KEYENCE_FOLDER;
            FILE_PATH_BROWSER.Filter           = "CSV files (*.csv)|*.csv|All files (*.*)|*.*";
            FILE_PATH_BROWSER.RestoreDirectory = true;
            EVENTS.LOG_MESSAGE(3, "Launched open file dialog.");
            if (FILE_PATH_BROWSER.ShowDialog() == DialogResult.OK) //Show the dialog. If the result is good...
            {
                TXT_BOX_KEYENCE.Text = FILE_PATH_BROWSER.FileName; //Store the filename
            }
            else //If the user did not select anything...
            {
                EVENTS.LOG_MESSAGE(3, "No file selected.");
                EVENTS.LOG_MESSAGE(1, "EXIT_FAIL");
                return;
            }
            bool IS_VALID = BACKEND.VALIDATE_FILE(TXT_BOX_KEYENCE.Text); //Check if the file is valid.

            if (IS_VALID)                                                //If it is valid...
            {
                LOAD_CSV_DATA(null, null);                               //Load in the data.
            }
            else
            {
                EVENTS.LOG_MESSAGE(1, "EXIT_FAIL");
                return;
            }
            EVENTS.LOG_MESSAGE(1, "EXIT_SUCCESS");
        }
        /// <summary>
        /// This method loads CSV data in from the TXT_BOX_KEYENCE.Text and binds it to DGV1. It does some controls formatting as well.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LOAD_CSV_DATA(object sender, EventArgs e) //Logged and documented.
        {
            EVENTS.LOG_MESSAGE(1, "ENTER");

            //Load in data.
            bool IS_VALID = BACKEND.VALIDATE_FILE(TXT_BOX_KEYENCE.Text); //Check to make sure the filepath is valid.

            if (!IS_VALID)                                               //If the file is not valid...
            {
                EVENTS.LOG_MESSAGE(1, "EXIT_FAIL");
                return;                                                //Break out of method. VALIDATE_FILE method will notify user of problems.
            }
            TABLE_PROCESSOR KEYENCE_PROCESSOR = new TABLE_PROCESSOR(); //Create a table processor.

            EVENTS.LOG_MESSAGE(3, "Created a new TABLE_PROCESSOR.");
            DataTable GRID_DATA    = new DataTable();                                                                      //Create a datatable that will hold all the csv data.
            DataTable INSTRUCTIONS = INSTRUCTION_SET.CREATE_INSTRUCTION_TABLE();                                           //We need to create a simple instruction table to load in the file.

            KEYENCE_PROCESSOR.PROCESS_INSTRUCTIONS(ref GRID_DATA, ref INSTRUCTIONS, TXT_BOX_KEYENCE.Text, ',', null, ','); //Load in the data into GRID_DATA.
            EVENTS.LOG_MESSAGE(3, "Data loaded in.");
            EVENTS.LOG_MESSAGE(3, "Naming each column.");
            foreach (DataColumn COLUMN in GRID_DATA.Columns) //Modification to prevent some null references later on.
            {
                COLUMN.ColumnName = COLUMN.Ordinal.ToString();
            }

            DGV1.DataSource = GRID_DATA; //Bind the DGV to the data. The DGV will be where we can pull the table from on future calls.
            EVENTS.LOG_MESSAGE(3, "Data bound to DGV.");
            //Format DGV.
            DGV1.RowHeadersVisible        = false;
            DGV1.ColumnHeadersVisible     = true;
            DGV1.ReadOnly                 = true;
            DGV1.AllowUserToOrderColumns  = false;
            DGV1.AllowUserToResizeRows    = false;
            DGV1.AllowUserToResizeColumns = false;
            foreach (DataGridViewColumn COLUMN in DGV1.Columns)
            {
                COLUMN.SortMode = DataGridViewColumnSortMode.NotSortable;
            }
            EVENTS.LOG_MESSAGE(3, "Formatted DGV.");
            //Enable controls for working with table.
            GRP_BOX_COLUMN_ASSIGNERS.Enabled = true;
            EVENTS.LOG_MESSAGE(3, "Assigner controls enabled.");
            EVENTS.LOG_MESSAGE(1, "EXIT_SUCCESS");
        }
        /// <summary>
        /// This method handles checking that all data is good for saving. It is called when the save button is clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BTN_SAVE_CLICK(object sender, EventArgs e) //Logged and documented.
        {
            EVENTS.LOG_MESSAGE(1, "ENTER");

            //Check the part number is good----------------------------------------------------------------------
            string TEXT   = TXT_BOX_PART_NUM.Text;                  //Get the canidate string from the textbox.
            int    RESULT = BACKEND.VALIDATE_PART_NUMBER(ref TEXT); //Check the string validity.

            TXT_BOX_PART_NUM.Text = TEXT;                           //Reassign the string to the textbox so its capitalized.
            if (RESULT != 0)                                        //If VALIDATE_PART_NUMBER did not execute successfully...
            {
                string MESSAGE = null;
                switch (RESULT) //Determine exactly what happened and throw the appropriate message.
                {
                case (1):
                    MESSAGE = "The part number is not valid. It must be six (6) characters long.";
                    MessageBox.Show(MESSAGE, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    break;

                case (2):
                    MESSAGE = "The part number is not valid. The first two characters need to be letters.";
                    MessageBox.Show(MESSAGE, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    break;

                case (3):
                    MESSAGE = "The part number is not valid. The last four characters need to be digits.";
                    MessageBox.Show(MESSAGE, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    break;
                }
                EVENTS.LOG_MESSAGE(2, "EXCEPTION", MESSAGE);
                EVENTS.LOG_MESSAGE(1, "EXIT_FAIL");
                return; //Cancel all other operations.
            }
            EVENTS.LOG_MESSAGE(3, "Part number is good.");

            //Check that a checksheet type is selected-----------------------------------------------------------
            string CHECKSHEET_TYPE = "";

            try
            {
                CHECKSHEET_TYPE = LISTBOX_CHECKSHEET_TYPE.SelectedItem.ToString();
            }
            catch (NullReferenceException)
            {
                string MESSAGE = "Please select a checksheet type.";
                MessageBox.Show(MESSAGE, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                EVENTS.LOG_MESSAGE(2, "EXCEPTION", MESSAGE);
                EVENTS.LOG_MESSAGE(1, "EXIT_FAIL");
                return; //Cancel all other operations.
            }
            EVENTS.LOG_MESSAGE(3, "Sheet type is good.");

            //Check if the keyence csv path is valid-------------------------------------------------------------
            bool IS_CSV_VALID = BACKEND.VALIDATE_FILE(TXT_BOX_KEYENCE.Text);

            if (!IS_CSV_VALID)
            {
                return;
            }
            EVENTS.LOG_MESSAGE(3, "Path is valid.");

            //Check that data has been previewed in the viewer---------------------------------------------------
            //This bit should no longer be relevant because I changed the code to automatically load whatever
            //the filepath is, but it can't hurt to keep it in case I did not think of something.
            if (DGV1.Rows.Count <= 0)
            {
                string MESSAGE = "The Keyence CSV file has not been opened and assignments have not been made.";
                MessageBox.Show(MESSAGE, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                EVENTS.LOG_MESSAGE(2, "EXCEPTION", MESSAGE);
                EVENTS.LOG_MESSAGE(1, "EXIT_FAIL");
                return; //Cancel all other operations.
            }

            //Get the DGV table for the next couple steps--------------------------------------------------------
            DataTable TABLE   = new DataTable();
            bool      SUCCESS = BACKEND.EXTRACT_DATA_TABLE_FROM_DGV(ref DGV1, ref TABLE);

            EVENTS.LOG_MESSAGE(3, "Retrieved DataTable.");

            //Check that a timestamp column is selected----------------------------------------------------------
            int TIMESTAMP_INDEX = -1;

            foreach (DataColumn COLUMN in TABLE.Columns)
            {
                if (COLUMN.ColumnName == "TimeStamp")
                {
                    TIMESTAMP_INDEX = COLUMN.Ordinal;
                }
            }
            if (TIMESTAMP_INDEX == -1)
            {
                string MESSAGE = "A timestamp column was not designated, this is required.";
                MessageBox.Show(MESSAGE, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                EVENTS.LOG_MESSAGE(2, "EXCEPTION", MESSAGE);
                EVENTS.LOG_MESSAGE(1, "EXIT_FAIL");
                return; //Cancel all other operations.
            }

            //Generate a string of all the keystrokes------------------------------------------------------------
            string KEY_SEQUENCE = "";

            foreach (string KEY in SEQUENCE_LIST)
            {
                KEY_SEQUENCE += KEY + ",";
            }
            EVENTS.LOG_MESSAGE(3, "Key sequence generated.");

            //Commit the data to the returning package-----------------------------------------------------------
            RECIPE_DATA DATA = new RECIPE_DATA();

            DATA.part_number     = TXT_BOX_PART_NUM.Text;
            DATA.checksheet_type = CHECKSHEET_TYPE;
            DATA.key_sequence    = KEY_SEQUENCE;
            DATA.csv_location    = TXT_BOX_KEYENCE.Text;
            DATA.timestamp_col   = TIMESTAMP_INDEX;
            DATA.a_col           = -1;
            DATA.b_col           = -1;
            DATA.c_col           = -1;
            DATA.d_col           = -1;
            DATA.e_col           = -1;
            DATA.f_col           = -1;
            DATA.g_col           = -1;
            DATA.h_col           = -1;
            DATA.i_col           = -1;
            DATA.j_col           = -1;
            DATA.k_col           = -1;
            DATA.l_col           = -1;
            DATA.m_col           = -1;
            if (SUCCESS)
            {
                foreach (DataColumn COLUMN in TABLE.Columns)
                {
                    switch (COLUMN.ColumnName)
                    {
                    case ("A"):
                        DATA.a_col = COLUMN.Ordinal;
                        break;

                    case ("B"):
                        DATA.b_col = COLUMN.Ordinal;
                        break;

                    case ("C"):
                        DATA.c_col = COLUMN.Ordinal;
                        break;

                    case ("D"):
                        DATA.d_col = COLUMN.Ordinal;
                        break;

                    case ("E"):
                        DATA.e_col = COLUMN.Ordinal;
                        break;

                    case ("F"):
                        DATA.f_col = COLUMN.Ordinal;
                        break;

                    case ("G"):
                        DATA.g_col = COLUMN.Ordinal;
                        break;

                    case ("H"):
                        DATA.h_col = COLUMN.Ordinal;
                        break;

                    case ("I"):
                        DATA.i_col = COLUMN.Ordinal;
                        break;

                    case ("J"):
                        DATA.j_col = COLUMN.Ordinal;
                        break;

                    case ("K"):
                        DATA.k_col = COLUMN.Ordinal;
                        break;

                    case ("L"):
                        DATA.l_col = COLUMN.Ordinal;
                        break;

                    case ("M"):
                        DATA.m_col = COLUMN.Ordinal;
                        break;
                    }
                }
                EVENTS.LOG_MESSAGE(3, "Columns assigned.");
            }
            else
            {
                string MESSAGE = "Failed to extract DGV Data";
                MessageBox.Show(MESSAGE, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                EVENTS.LOG_MESSAGE(2, "EXCEPTION", MESSAGE);
                EVENTS.LOG_MESSAGE(1, "EXIT_FAIL");
            }

            //Invoke delegate with info pack and close-----------------------------------------------------------
            DATA_READY?.Invoke(null, DATA);
            EVENTS.LOG_MESSAGE(1, "EXIT_SUCCESS");
        }