AOI_Saki_SaveProcessData.cs

*AOI_Saki_SaveProcessData.cs*

using Bartech.SGCore.Base;
using Bartech.SGCore.Helpers;
using Bartech.SGCore.Local;
using Bartech.SGCore.Local.Services;
using Bartech.SGCore.Logic.NVCZ.Modules.AOI.Model;
using Bartech.SGCore.Logic.NVCZ.Modules.LaserParser.Model;
using DevExpress.Xpo;
using DevExpress.Xpo.DB;
using DevExpress.Xpo.Metadata;
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using xTrace03_SG_Data;
using PartDataStatus = Bartech.SGCore.Local.DataObjects.PartDataStatus;
using xdata = xTrace03_SG_Data;

namespace Bartech.SGCore.Logic.NVCZ.Modules.AOI
{
    [XModule("NVCZ_AOI_Saki_SaveData", "Modul pro ukladani procesnich dat z AOI Saki", ModuleGroupType.External, applicationType: xTraceApplicationType.NvCzAOI)]
    public class AOI_Saki_SaveProcessData : SGModuleLocal
    {
        #region Constructor
        public AOI_Saki_SaveProcessData(SGSession session, MSSQL cData)
            : base(session)
        {
            m_cData = cData;
            Session.Initialized += new EventHandler((s, e) =>
            {
                InitXPO(m_cData);
                m_dxSession = Session.Resolve<SGDxSessionLocker>();
            });
            m_aoiSakiSvc = Session.Resolve<AoiSakiService>();
        }
        #endregion

        #region Private variables
        private AoiSakiService m_aoiSakiSvc;
        private MSSQL m_cData;
        private IDataLayer m_cDataDL;

        private SGDxSessionLocker m_dxSession;
        private string m_divrgCode = "F00001";
        private SGDivergenceItem m_divrg;
        private int m_resizedImagePercents = 80;
        private string m_csvFilePath = string.Empty;
        private string m_csvOriginalFilePath = string.Empty;

        private string m_TestOK = "Ok";
        private string m_addToImageName = "_Color";
        private string m_ngTestFilePath = string.Empty;
        private bool m_changed = false;
        private string m_panelPhotoFileName = "CURRENTA.jpg";
        private int m_aoiNgGSStatus = 35;
        private SavePanelImage m_savePanelImage = SavePanelImage.Error;
        private int m_addToCsvFileNameInterval = 3;

        private bool m_sendDPMOAlarm = true;
        private int m_boundDPMO = 0;
        private string m_DPMOKatalogParam = string.Empty;
        private string m_DPMOAlarmCode = "A20043";

        private bool m_sendFalseErrorAlarm = true;
        private int m_boundFalseError = 0;
        private string m_falseErrorKatalogParam = string.Empty;
        private string m_falseErrorAlarmCode = "A20042";
        private int falseErrorCheckInterval = 15;
        private AOIAlarmVariables alarmVariablesFalseErrors;
        private AOIAlarmVariables alarmVariablesDPMO;
        private Dictionary<long, AOIGUIHelp> subBoardNotes = new Dictionary<long, AOIGUIHelp>();
        private bool m_showMatrix = true;
        private string m_correctBarcode = string.Empty;
        private bool m_useCsvFileForOpportunities = false;
        #endregion

        #region Constants
        private const string PASS = "PASS";
        private const string FAIL = "FAIL";
        private const string OK = "Ok";
        #endregion

        #region Module overrides
        protected override bool OnSetParameters()
        {
            m_divrgCode = Session.SysParams.TryGetString(this, "DivrgBarcode", m_divrgCode, "Barkód chyby, která se zapíše v případě chyby.");
            m_ngTestFilePath = Session.SysParams.TryGetString(this, "FilePathNgTest", m_ngTestFilePath, "Cesta ke složkám se zákazníky ve kterých se nacházejí výsledky testů panelů u kterých něco rozhodoval operátor.");
            m_sendDPMOAlarm = Session.SysParams.GetBoolean(this, "SendDPMOAlarm", m_sendDPMOAlarm, "Jestli sa má odeslat alarm, kvůli KPI DPMO.");
            m_boundDPMO = Session.SysParams.GetParamValue<int>(this, "DPMOLimit", m_boundDPMO, "Nastavení limitu pro poslání alarmu z důvodu KPI DPMO. Pokud je hodnota 0, pak se bere hodnota z katalogového parametru určeného hodnotou DPMOKatalogParam.");
            m_DPMOKatalogParam = Session.SysParams.TryGetString(this, "DPMOKatalogParam", m_DPMOKatalogParam, "Katalogový parametr ze kterého se získá hodnota meze pro KPI DPMO.");
            m_DPMOAlarmCode = Session.SysParams.TryGetString(this, "DPMOAlarmCode", m_DPMOAlarmCode, "Kód alarmu, který se vyvolá při překročení meze DPMO.");
            m_sendFalseErrorAlarm = Session.SysParams.GetBoolean(this, "SendFalseErrorAlarm", m_sendFalseErrorAlarm, "Jestli se má odeslat alarm, pokud počet falešných chyb překročí určitou mez.");
            m_boundFalseError = Session.SysParams.GetParamValue<int>(this, "FalseErrorLimit", m_boundFalseError, "Nastavení limitu pro poslání alarmu z důvodu falešných chyb. Pokud je hodnota 0, pak se bere hodnota z katalogového parametru určeného hodnotou FalseErrorKatalogParam.");
            m_falseErrorKatalogParam = Session.SysParams.TryGetString(this, "FalseErrorKatalogParam", m_falseErrorKatalogParam, "Katalogový parametr ze kterého se získá hodnota meze pro falešné chyby.");
            m_falseErrorAlarmCode = Session.SysParams.TryGetString(this, "FalseErrorAlarmCode", m_falseErrorAlarmCode, "Kód alarmu, který se vyvolá při překročení meze počtu falešných chyb.");
            falseErrorCheckInterval = Session.SysParams.GetParamValue<int>(this, "AlarmInterval", falseErrorCheckInterval, "Nastavení v minutách po jaké době se mají posílat alarmy.");
            m_resizedImagePercents = Session.SysParams.GetInt32(this, "ResizedImagePercents", m_resizedImagePercents, "Hodnota udávající na kolik procent původního obrázku se má zmenšit komprimovaný obrázek.");
            m_csvFilePath = Session.SysParams.TryGetString(this, "FilePathCSV", m_csvFilePath, "Cesta k adresářům, jejichž název je ve formátu yyyy-mm, ve kterých se nacházejí csv soubory.");
            m_panelPhotoFileName = Session.SysParams.TryGetString(this, "PanelPhotoFileName", m_panelPhotoFileName, "Jméno souboru s příponou, jenž obsahuje fotku celého panelu.");
            m_aoiNgGSStatus = Session.SysParams.GetInt32(this, "AoiNgGSStatus", m_aoiNgGSStatus, "Hodnota udávající jaký status se nastaví dílu při NOT OK testu dílu na pracovišti.");
            m_savePanelImage = Session.SysParams.GetEnum(this, "SavePanelImage", m_savePanelImage, "Kdy ukládat fotku panelu. Hodnoty: Error - pouze pokud panel obsahuje opravdouvou chybu, FalseError - pokud panel obsahuje jakoukoliv chybu, NotSave - neukládat obrázek nikdy.");
            m_showMatrix = Session.SysParams.GetBoolean(this, "ShowMatrix", m_showMatrix, "Zobrazovat matici s OK/NOK PCB?");
            m_addToImageName = Session.SysParams.TryGetString(this, "AddToImageName", m_addToImageName, "Řetězec, který se vloží na konec názvu souboru s obrázkem chyby.");
            m_csvOriginalFilePath = Session.SysParams.TryGetString(this, "FilePathCsvOriginal", m_csvOriginalFilePath, "Cesta k adresáři do kterého AOI generuje soubory typu csv.");
            m_addToCsvFileNameInterval = Session.SysParams.GetInt32(this, "CsvTimeFileInterval", m_addToCsvFileNameInterval, "Hodnota udávající kolik vteřin bude postupně přičtěno/odečteno od časové hodnoty v bfre souboru v případě, že nebude nalezen soubor s originální hodnotou.");
            m_useCsvFileForOpportunities = Session.SysParams.GetBoolean(this, "UseCsvFileForOpportunities", m_useCsvFileForOpportunities, "Parametr rozhodující o tom zda se při pokusu o získání počtu opportunities na rozhodovacím terminálu použije hledání csv souboru.");

            if (Session.ProcessContext.SakiTypeOfWorkPlace == TypeOfWorkPlace.DecisionTerminal) // pokud jsme na rozhodovacím pracovišti, tak musí existovat složky s výsledky, je to schválně ve dvou if-kách, aby bylo poznat která složka neexistuje
            {
                if (Directory.Exists(m_ngTestFilePath) == false)
                {
                    Message = Log(LogMsg.FILEGUARD_DirectoryNotFound, m_ngTestFilePath);
                    return false;
                }
                if (Directory.Exists(m_csvFilePath) == false)
                {
                    Message = Log(LogMsg.FILEGUARD_DirectoryNotFound, m_csvFilePath);
                    return false;
                }
                if (m_addToCsvFileNameInterval < 0)
                {
                    Message = Log(LogMsg.WORKPLACEPARAMETER_NotCorrectFormat, "CsvTimeFileInterval", "NVCZ AOI Saki SaveData");
                    return false;
                }

                Session.ReworkLists.LoadDivergencesAll();
                var list = Session.ReworkLists.DivergencesAll.Where(p => p.Key == m_divrgCode);
                try { m_divrg = list.First().Value; } catch (Exception ex) { throw new Exception("Unknown Divrg barcode", ex); }
            }

            if (!string.IsNullOrEmpty(m_ngTestFilePath))
            {
                Session.ProcessContext.SakiNgFilePath = m_ngTestFilePath;
            }

            return base.OnSetParameters();
        }

        public override SGLMState ModuleChecker(SGTreeModuleData mdata)
        {
            if (Session.ProcessContext.SakiTypeOfWorkPlace == TypeOfWorkPlace.Machine)
            {
                if (Session.ProcessContext.SakiData.IsBypassMode == true)
                {
                    Session.WorkPlaceSetting.InfoData.LastPartBarcode = Session.ProcessContext.SakiData.ManageCode;
                    return SGLMState.Handled | SGLMState.OK | SGLMState.UpLevel;
                }
            }
            return base.ModuleChecker(mdata);
        }

        public override SGLMState HandlePortData(SGTreeModuleData mdata, SGReceivedData data)
        {
            SGLMState s = SGLMState.NotHandled;

            if (mdata.LevelState == SGLevelState.Echo)
            {
                if (Session.Part.DoPart.IsPartStarted)
                {
                    switch (Session.ProcessContext.SakiTypeOfWorkPlace)
                    {
                        case TypeOfWorkPlace.Machine:
                            s = AOICheckWorkplace();
                            break;
                        case TypeOfWorkPlace.DecisionTerminal:
                            s = AOIDeciderWorkplace(Session.Part.DoPart.CurBarcode);
                            break;
                        default:
                            break;
                    }
                }
                else
                {
                    Log(LogMsg.CUSTOM_WARNING_MSG, $"NEPOVOLENÁ AKCE: Není nastartovaný díl. Modul NVCZ AOI SAKI SAVEDATA, Echo, typ pracoviště={Session.ProcessContext.SakiTypeOfWorkPlace.ToString()}");
                    s = SGLMState.Handled | SGLMState.Fail | SGLMState.ResetLevel;
                }
            }
            else if (mdata.LevelState == SGLevelState.WaitingForData)
            {
                Log(LogMsg.CUSTOM_WARNING_MSG, $"NEPOVOLENÁ AKCE: Není nastartovaný díl. Modul NVCZ AOI SAKI SAVEDATA, WaitingForData, typ pracoviště={Session.ProcessContext.SakiTypeOfWorkPlace.ToString()}");
                s = SGLMState.Handled | SGLMState.Fail | SGLMState.ResetLevel;
            }
            return s;
        }
        #endregion

        #region Helper functions
        private SGLMState AOIDeciderWorkplace(string startedBarcode)
        {
            SGLMState s = SGLMState.NotHandled;
            if (Session.ProcessContext.AOI_Data.IsJobChanged == true)
            {
                Session.ProcessContext.AOI_Data.IsJobChanged = false;
                InitializeAlarms();
            }

            Tuple<string, bool> barcode = new Tuple<string, bool>(startedBarcode, false);
            string bcd = string.Empty;
            string barcodeWithoutSpecialCharacter = string.Empty;

            if (Session.ProcessContext.EscapeHelper.IsSerialNumberNormalized(startedBarcode, ref bcd))
            {
                barcode = new Tuple<string, bool>(bcd, true);
            }

            string tempPathToFolder = Path.Combine(m_ngTestFilePath, Session.ProcessContext.SakiCustomer, Session.ProcessContext.SakiProgramName, "Lot");
            if (Directory.Exists(tempPathToFolder) == false)
            {
                Message = Log(LogMsg.AOISAKI_DirectoryWithResultDoesNotExists, tempPathToFolder);
                return SGLMState.Handled | SGLMState.Fail | SGLMState.ResetLevel;
            }
            Log(LogMsg.CUSTOM_INFO_MSG, $"Hledám všechny složky na cestě: {tempPathToFolder}");
            List<string> directories = Directory.GetDirectories(tempPathToFolder).ToList();

            StringBuilder sb = new StringBuilder();
            foreach (var item in directories)
            {
                sb.Append(Path.GetFileName(item));
                sb.Append(", ");
            }
            Log(LogMsg.CUSTOM_INFO_MSG, $"Seznam všech složek v adresáři {tempPathToFolder}: {sb.ToString()}");
            sb.Clear();
            List<string> directoriesWithBarcode = new List<string>();
            DateTime lastWriteTime = DateTime.MinValue;
            int indexOfDirectory = 0;

            Log(LogMsg.CUSTOM_INFO_MSG, $"Hledám složku jejíž název obsahuje {barcode.Item1} ");
            foreach (string path in directories)
            {
                if (path.Contains(barcode.Item1))
                {
                    directoriesWithBarcode.Add(path);
                }
            }
            if (directoriesWithBarcode.Count() == 0)
            {
                Log(LogMsg.CUSTOM_INFO_MSG, "Žádná složka nebyla nalezena. Zkusíme ostatní sériová čísla na panelu.");
                // nenašly jsme složku, která odpovídá načtenému SN, zkusíme ostatní SN na panelu
                barcode = FindCorrectBarcode(PrepareBarcodes(GetAllPossibleBarcodes()), directories);
                if (barcode is null)
                {
                    // složka neexistuje
                    s |= SGLMState.ResetLevel;
                    Session.Part.Clear();
                    Message = Log(LogMsg.AOI_DirectoryNotFound);
                    return s;
                }
                foreach (string path in directories) // našli jsme barcode, který odpovídá názvu nějaké složky
                {
                    if (path.Contains(barcode.Item1))
                    {
                        directoriesWithBarcode.Add(path);
                    }
                }
            }
            for (int i = 0; i < directoriesWithBarcode.Count(); i++) // hledání složky, s nejnovějším časem modifikace
            {
                DirectoryInfo dirInfo = new DirectoryInfo(directoriesWithBarcode[i]);
                if (dirInfo.LastWriteTimeUtc > lastWriteTime)
                {
                    lastWriteTime = dirInfo.LastWriteTimeUtc;
                    indexOfDirectory = i;
                }
            }
            Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Nalezena složka {directoriesWithBarcode[indexOfDirectory]}");
            m_correctBarcode = GetCorrectBarcode(barcode, directoriesWithBarcode[indexOfDirectory]);

            // sestaveni spravne cesty k souborum se spravnymi nazvy
            string bfreFile = string.Concat("DATA_", m_correctBarcode, ".bfre");
            string bzmdFile = string.Concat(m_correctBarcode, ".bzmd");
            string pathToFolder = Path.Combine(m_ngTestFilePath, Session.ProcessContext.SakiCustomer, Session.ProcessContext.SakiProgramName, Path.Combine("Lot", m_correctBarcode));
            string pathToBfreFile = Path.Combine(pathToFolder, bfreFile);
            string pathToBzmdFile = Path.Combine(pathToFolder, bzmdFile);

            Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Hledám soubor {pathToBzmdFile} a {pathToBfreFile}");
            bool filesExists = false;
            int numOfTries = Session.ProcessContext.NumOfTriesForOpenFile;
            do
            {
                if ((File.Exists(pathToBfreFile) == false) || (File.Exists(pathToBzmdFile) == false))
                {
                    Thread.Sleep(Session.ProcessContext.WaitTimeBeforeAnotherTryToOpenFile);
                    numOfTries--;
                    Log(LogMsg.CUSTOM_WARNING_MSG, $"Nenašel jsem bfre nebo bzmd soubor. Počet zbývajících pokusů: {numOfTries}");
                }
                else
                {
                    filesExists = true;
                }
            } while (!filesExists && (numOfTries > 0));

            if (!filesExists)
            {
                s |= SGLMState.ResetLevel;
                Session.Part.Clear();
                if (Directory.Exists(pathToFolder))
                {
                    try
                    {
                        Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Obsah složky {pathToFolder} je {string.Join(",\n", Directory.GetFiles(pathToFolder))}");
                    }
                    catch (Exception e)
                    {
                        Message = Log(LogMsg.CUSTOM_WARNING_MSG, $"Nepodařilo se získat soubory ze složky {pathToFolder} chyba typu {e.Message}");
                    }
                }

                Message = Log(LogMsg.AOI_FileNotFound);
                return s;
            }

            SakiResult dataFromFiles = new SakiResult();

            Message = Log(LogMsg.FILE_TryOpen, pathToBfreFile);
            if (FileHelper.TryParseFile(dataFromFiles.BfreData.ParseBfreFile, pathToBfreFile, Session.ProcessContext.NumOfTriesForOpenFile, Session.ProcessContext.WaitTimeBeforeAnotherTryToOpenFile, LogErrorFromTryOpenFile) == false) // pro zjisteni zda jiz nebylo rozhodnuto
            {
                s |= SGLMState.ResetLevel;
                Session.Part.Clear();
                Message = Log(LogMsg.FILE_ERROR, pathToBfreFile);
                Session.Part.Clear();
                return s;
            }

            if (dataFromFiles.BfreData.JudgementResult == OK) // nekdo z nejakeho duvodu nacetl dil, ktery byl vyhodnocen strojem jako OK
            {
                s |= SGLMState.OK | SGLMState.UpLevel;
                Session.Part.DoPart.SaveProcessData("Byl kontrolován již OK díl!", string.Empty, PartDataStatus.OK);
                return s;
            }

            Message = Log(LogMsg.FILE_TryOpen, pathToBzmdFile);
            if (FileHelper.TryParseFile(dataFromFiles.BzmdData.ParseBzmdFile, pathToBzmdFile, false, Session.ProcessContext.NumOfTriesForOpenFile, Session.ProcessContext.WaitTimeBeforeAnotherTryToOpenFile, LogErrorFromTryOpenFile) == false) // pro vypsani udaju kolik je potrebnych rozhodnuti operatora
            {
                s |= SGLMState.ResetLevel;
                Message = Log(LogMsg.FILE_ERROR, pathToBfreFile);
                Session.Part.Clear();
                return s;
            }

            if (WaitForFileUpdate(dataFromFiles, pathToFolder, bfreFile, pathToBfreFile) == false) // cekame dokud operator nerozhodne o povaze chyb
            {
                s |= SGLMState.ResetLevel;
                Session.Part.Clear();
                Message = Log(LogMsg.FILE_ERROR, pathToBfreFile + " zpracování dílu bylo přerušeno.");
                Session.Part.Clear();
                return s;
            }
            Log(LogMsg.FILE_ReadFile, pathToBfreFile, "bfre");
            if (dataFromFiles.BfreData == null)
            {
                s |= SGLMState.ResetLevel;
                Session.Part.Clear();
                Message = Log(LogMsg.FILE_ERROR, pathToBfreFile + " nepodařilo se zpracovat BFRE soubor.");
                Session.Part.Clear();
                return s;
            }

            SetErrorVariables(dataFromFiles);

            // ziskani aktualizovanych udaju o povaze chyb po rozhodnuti operatora
            dataFromFiles.BzmdData.Clear();
            Message = Log(LogMsg.FILE_TryOpen, pathToBfreFile);
            if (FileHelper.TryParseFile(dataFromFiles.BzmdData.ParseBzmdFile, pathToBzmdFile, false, Session.ProcessContext.NumOfTriesForOpenFile, Session.ProcessContext.WaitTimeBeforeAnotherTryToOpenFile, LogErrorFromTryOpenFile) == false) // pro zjisteni zda jiz nebylo rozhodnuto
            {
                s |= SGLMState.ResetLevel;
                Session.Part.Clear();
                Message = Log(LogMsg.FILE_ERROR, string.Concat(pathToBfreFile, " druhe cteni"));
                Session.Part.Clear();
                return s;
            }
            Log(LogMsg.FILE_ReadFile, pathToBzmdFile, "bzmd");

            subBoardNotes.Clear();
            Session.ProcessContext.PcbDirection.PanelParts = Session.Part.DoPart.GetPanelParts();

            // zapis chyb
            bool isPanelOk = true; // pro zjištění zda se bude ukládat fotka celého panelu, ve které by měly být vyznačeny chyby
            foreach (SakiTestResult oneTest in dataFromFiles.BzmdData.TestedPos.Where(x => x.ErrorWrite == true))
            {
                long subBoardOID = Session.Part.DoPart.PartID;
                int subBoardNum = 0;
                long correctTestIndex = 0;
                if (Int32.TryParse(oneTest.SubReference, out subBoardNum) == false)
                {
                    subBoardNum = 0;
                    Message = Log(LogMsg.AOI_NotParsedSubReference, oneTest.SubReference);
                }

                if (subBoardNum == 0)
                {
                    // na sub_reference rovno 0 by měli být pouze nezajímavé informace ohledně panelu a badmarků
                    continue;
                }

                try
                {
                    correctTestIndex = Session.ProcessContext.PcbDirection.GetTestIndex(subBoardNum - 1) - 1;
                    subBoardOID = Session.ProcessContext.PcbDirection.GetPartOID(correctTestIndex);
                }
                catch (Exception)
                {
                    correctTestIndex = 0;
                    subBoardOID = Session.Part.DoPart.PartID;
                    Message = Log(LogMsg.PCBDIRECTION_PcbIndexNotFinded, subBoardNum);
                }

                WriteDivergence(oneTest.RefName, oneTest.ErrorType, oneTest.Bitmap, pathToFolder, oneTest.Result, subBoardOID);
                if (oneTest.Result == FAIL)
                {
                    //WriteDivrg((int)(correctTestIndex), oneTest);
                    isPanelOk = false; // alespoň jedna chyba byla vyhodnocena jak opravdová => je potřeba uložit fotku celého panelu
                }
            }

            if (m_showMatrix == true)
            {
                Session.ProcessContext.AOI_Data.IsInformationLoaded = true; // zviditelneni jednotlivych desek v GUI
            }

            // hledání csv souboru, aby byl korektni pocet opportunities, pokud nenaleznu tak pouziju hodnotu z bzmd souboru, protoze je to lepsi jak nic   
            if (Int32.TryParse(dataFromFiles.BzmdData.Opportunities, out int numOfOpportunities) == false)
            {
                numOfOpportunities = 1;
            }
            bool isCsvFileFinded = false;
            if (m_useCsvFileForOpportunities)
            {
                Tuple<bool, string> csvEx = CsvFileExists(GetCsvFilePaths(dataFromFiles.BfreData.CsvFileName, CorrectBackupFolder(dataFromFiles.BfreData.CsvDateTime)), dataFromFiles.BfreData, SearchCsvFileWithTimeOffset);
                if (csvEx.Item1 == true)
                {
                    Message = Log(LogMsg.FILE_TryOpen, csvEx.Item2);
                    if (FileHelper.TryParseFile(dataFromFiles.CsvData.ParseCsvFile, csvEx.Item2, Session.ProcessContext.NumOfTriesForOpenFile, Session.ProcessContext.WaitTimeBeforeAnotherTryToOpenFile, LogErrorFromTryOpenFile) == true)
                    {
                        dataFromFiles.BzmdData.Opportunities = dataFromFiles.CsvData.NumberOfTests.ToString();
                        numOfOpportunities = dataFromFiles.CsvData.NumberOfTests;
                        isCsvFileFinded = true;
                    }
                }
            }

            if (!m_useCsvFileForOpportunities || (m_useCsvFileForOpportunities && !isCsvFileFinded))
            {
                if (m_useCsvFileForOpportunities) Message = Log(LogMsg.CUSTOM_WARNING_MSG, "Nepodařilo se najít soubor csv. Pokusíme se počet opportunities nalézt v databázi.");

                int opportunities = m_aoiSakiSvc.GetOpportunitiesForPartID(m_cDataDL, (ex) => Message = Log(LogMsg.CUSTOM_WARNING_MSG, $"V databázi se nepodařilo najít záznam pro PartID: {Session.Part.DoPart.PartID}, JobID: {Session.Job.GetEffectiveDOJob().JobID}, JobRoutingPosIndex: {Session.CycleContext.OperationOverride.RoutingPosIndex}. Chyba {ex.Message}"), Session.Part.DoPart.PartID, Session.Job.GetEffectiveDOJob().JobID, Session.CycleContext.OperationOverride.RoutingPosIndex);
                if (opportunities > 0)
                {
                    numOfOpportunities = opportunities;
                }
                else // údaj není ani v db
                {
                    Message = Log(LogMsg.AOISAKI_OpportunitiesFromBfre);
                    Message = Log(LogMsg.AOISAKI_CsvFileNotExists, dataFromFiles.BfreData.CsvFileName);
                }
            }

            // aktualizace pocitadel pro alarmy
            alarmVariablesFalseErrors.AddToVariables(numOfOpportunities, dataFromFiles.BzmdData.CountedResultFalseErrors, dataFromFiles.BzmdData.CountedResultError);
            alarmVariablesDPMO.AddToVariables(numOfOpportunities, dataFromFiles.BzmdData.CountedResultFalseErrors, dataFromFiles.BzmdData.CountedResultError);

            // pocitani zda se ma poslat alarm
            FalseErrorAlarm(alarmVariablesFalseErrors);
            DPMOAlarm(alarmVariablesDPMO);

            // uložení obrázku celého panelu
            SaveImageOfPanel(isPanelOk, pathToFolder);

            SaveProcessData(dataFromFiles.BzmdData);

            dataFromFiles.CsvData.Clear();

            s |= SGLMState.OK | SGLMState.UpLevel;
            return s;
        }

        private string GetCorrectBarcode(Tuple<string, bool> barcode, string correctDirectory)
        {
            Regex numberRegex = new Regex(string.Concat(barcode.Item1, @".*\((?<NumOfTest>[0-9]+)\)"));
            if (numberRegex.IsMatch(correctDirectory)) // je to slozka s cislem testu
            {
                if (barcode.Item2) // SN je escapovano
                {
                    return string.Concat("_{", barcode.Item1, "(", numberRegex.Match(correctDirectory).Groups["NumOfTest"].Value, ")}");
                }
                else
                {
                    return string.Concat(barcode.Item1, "(", numberRegex.Match(correctDirectory).Groups["NumOfTest"].Value, ")");
                }
            }
            else // je to slozka bez cisla testu
            {
                if (barcode.Item2)
                {
                    return string.Concat("_{", barcode.Item1, "}");
                }
                else
                {
                    return barcode.Item1;
                }
            }
        }

        /// <summary>   
        /// Metoda pro určení správné relativní cesty k zíloze souboru. 
        /// </summary>  
        /// <param name="dt"> Datum ze kterého se bere korektní složka se zálohou. </param> 
        /// <returns> Relativní část cesty označující cestu od původní složky ke složkám s měsíčními zálohami. Např. z cesty "C:\aoi\csv\backup\2020-10" je hodnota "backup\2020-10" </returns> 
        private string CorrectBackupFolder(DateTime dt)
        {
            return Path.Combine(m_csvFilePath, dt.ToString("yyyy-MM"));
        }

        /// <summary>   
        /// Metoda pro sestavení korektní cesty k souboru typu csv. Vrací cestu normální i cestu kde by měl být soubor zálohovaný.  
        /// </summary>  
        /// <param name="fileName"> Název csv souboru včetně přípony. </param>  
        /// <param name="correctBackupFolder"> Relativní část cesty označující cestu od původní složky ke složkám s měsíčními zálohami. Např. z cesty "C:\aoi\csv\backup\2020-10" je hodnota "backup\2020-10" </param>  
        /// <returns> Dvojice (string, string), kde první položka udává cestu k souboru na cestě se zálohou a druhý parametr udává cestu k souboru bez zálohy tj. tam kde se objevil. </returns>    
        private Tuple<string, string> GetCsvFilePaths(string fileName, string correctBackupFolder)
        {
            return new Tuple<string, string>(
                Path.Combine(correctBackupFolder, fileName),
                Path.Combine(m_csvOriginalFilePath, fileName));
        }

        /// <summary>   
        /// Metoda kontrolující existenci souboru z prvního parametru.  
        /// </summary>  
        /// <param name="csvLocations"> Dvojice (string, string), kde první položka udává cestu k souboru na cestě se zálohou a druhý parametr udává cestu k souboru bez zálohy tj. tam kde se objevil. </param>    
        /// <param name="dataFromBfreFile">  Objekt s daty získaných z bfre souboru. </param>   
        /// <param name="onFileNotExists"> Delegát pro zavolání funkce pro úpravu vyparserovaného času. </param>    
        /// <returns>  Dvojice (bool, string), kde první položka udává úspěšnost nalezení souboru a když je true, pak druhá položka obsahuje cestu k souboru. </returns>    
        private Tuple<bool, string> CsvFileExists(Tuple<string, string> csvLocations, SakiBfreData dataFromBfreFile, Func<SakiBfreData, Tuple<bool, string>> onFileNotExists = null)
        {
            Message = Log(LogMsg.FILE_SearchFile, csvLocations.Item1);
            if (File.Exists(csvLocations.Item1) == true)
            {
                return new Tuple<bool, string>(true, csvLocations.Item1);
            }
            Message = Log(LogMsg.FILE_SearchFile, csvLocations.Item2);
            if (File.Exists(csvLocations.Item2) == true)
            {
                return new Tuple<bool, string>(true, csvLocations.Item2);
            }
            if (onFileNotExists != null)
            {
                return onFileNotExists(dataFromBfreFile);
            }
            return new Tuple<bool, string>(false, "");
        }

        /// <summary>   
        /// Metoda, která slouží k úpravě času, podle kterého se hledá soubor typu csv. Postupně přičítá/odečítá sekundy od původního údaje. Tato úprava se provádí podle hodnoty parametru "AddToCsvFileName". 
        /// </summary>  
        /// <param name="dataFromBfreFile"> Objekt s daty získaných z bfre souboru. </param>    
        /// <returns> Dvojice (bool, string), kde první položka udává úspěšnost nalezení souboru a když je true, pak druhá položka obsahuje cestu k souboru. </returns> 
        private Tuple<bool, string> SearchCsvFileWithTimeOffset(SakiBfreData dataFromBfreFile)
        {
            if (dataFromBfreFile.CsvDateTime == DateTime.MinValue) // z nejakeho duvodu se nepodarilo ziskat data pro jmeno csv souboru 
            {
                return new Tuple<bool, string>(false, "");
            }
            DateTime tempFileDtName;
            Tuple<bool, string> val;
            for (int i = 1; i <= m_addToCsvFileNameInterval; i++)
            {
                tempFileDtName = dataFromBfreFile.CsvDateTime.AddSeconds(i);
                val = CsvFileExists(GetCsvFilePaths(GetCsvFileName(tempFileDtName), CorrectBackupFolder(tempFileDtName)), dataFromBfreFile);
                if (val.Item1 == true)
                {
                    return val;
                }
                tempFileDtName = dataFromBfreFile.CsvDateTime.AddSeconds(-i);
                val = CsvFileExists(GetCsvFilePaths(GetCsvFileName(tempFileDtName), CorrectBackupFolder(tempFileDtName)), dataFromBfreFile);
                if (val.Item1 == true)
                {
                    return val;
                }
            }
            return new Tuple<bool, string>(false, "");
        }

        /// <summary>   
        /// Metoda pro získání názvu csv souboru z údajů bfre souboru nebo upravený, protože soubor s údajem ze souboru neexistoval.    
        /// </summary>  
        /// <param name="dtOfFile"> Časový údaj získaný z bfre souboru nebo upravený, protože soubor s údajem ze souboru neexistoval. </param>  
        /// <returns> Název souboru včetně přípony. </returns>  
        private string GetCsvFileName(DateTime dtOfFile)
        {
            return string.Concat(dtOfFile.ToString("yyyyMMdd_HHmmss"), ".csv");
        }

        private void SaveProcessData(SakiBzmdData data)
        {
            if (data.CountedResultError == 0)
            {
                Session.Part.DoPart.MS = 1;
                Session.Part.DoPart.SaveProcessData(data.ToString(), string.Empty, PartDataStatus.OK);
            }
            else
            {
                Session.Part.DoPart.MS = m_aoiNgGSStatus;
                Session.Part.DoPart.SaveProcessData(data.ToString(), string.Empty, PartDataStatus.Fail);
            }
        }

        /// <summary>
        /// Metoda, ktera vytvori objekty, jenz si udrzuji hodnoty pro vypocet alarmu.
        /// </summary>
        private void InitializeAlarms()
        {
            double falseErrorBound = Convert.ToDouble(m_boundFalseError);
            double dpmoBound = Convert.ToDouble(m_boundDPMO);

            if (m_boundFalseError == 0)
            {
                falseErrorBound = Convert.ToDouble(Int32.Parse(Session.Job.DoProduct.GmsKatalogParams[m_falseErrorKatalogParam]));
            }
            alarmVariablesFalseErrors = new AOIAlarmVariables(falseErrorCheckInterval, falseErrorBound);
            Session.ProcessContext.AOI_Data.FalseErrorBound = falseErrorBound;

            if (m_boundDPMO == 0)
            {
                dpmoBound = Convert.ToDouble(Int32.Parse(Session.Job.DoProduct.GmsKatalogParams[m_DPMOKatalogParam]));
            }
            alarmVariablesDPMO = new AOIAlarmVariables(falseErrorCheckInterval, dpmoBound);
            Session.ProcessContext.AOI_Data.DpmoBound = dpmoBound;
        }

        /// <summary>
        /// Metoda starajici se o vyhledani spravneho kodu chyby, kodu pozice v databazi a naslednemu zapisu chyby do databaze.
        /// </summary>
        /// <param name="Position"> Pozice soucastky. </param>
        /// <param name="Result"> Kod chyby. </param>
        /// <param name="imagePath"> Cesta k obrazku. </param>
        /// <param name="pathToFolder"> Cesta ke slozce ve ktere se nachazi obrazek. </param>
        /// <param name="testComponentResult"> Vysledek testu. </param>
        /// <param name="partID"> ID partu ke kteremu chceme zapsat chybu. </param>
        private void WriteDivergence(string Position, string Result, string imagePath, string pathToFolder, string testComponentResult, long partID = 0)
        {
            string DivergenceCode = Result;
            if (Position == null) Position = string.Empty;
            string ComponentPositionID = Position;
            using (m_dxSession.GetLock())
            {
                // ziskani kodu divergence
                var DivgCode = new XPQuery<xdata.Divergence>(m_dxSession.Session).Where(d => d.MachineErrorCD == Result).Select(d => d.CommandBD).FirstOrDefault();
                if (!string.IsNullOrEmpty(DivgCode))
                {
                    DivergenceCode = DivgCode;
                }
                else // zjistena chyba neexistuje v databazi
                {
                    Log(LogMsg.MACHINEERROR_NotFound, Result, m_divrgCode);
                    DivergenceCode = m_divrgCode;
                }
                // zjisteni zda dana pozice jiz existuje nebo zda je potreba ji vytvorit
                long PositionInDB;
                var p = (new XPQuery<xdata.Positions>(m_dxSession.Session)).FirstOrDefault(x => x.PositionCD == ComponentPositionID);
                if (p == null)
                {
                    // pokud dana pozice jeste neni v DB tak potreba vytvorit
                    xdata.Positions pos = new xdata.Positions(m_dxSession.Session);
                    pos.PositionCD = ComponentPositionID;
                    pos.PositionType = m_dxSession.Session.GetObjectByKey<xdata.PositionsType>(1L);
                    pos.Save();

                    Log(LogMsg.COMPONENT_PositionCreated, ComponentPositionID);

                    var position = (new XPQuery<xdata.Positions>(m_dxSession.Session)).FirstOrDefault(x => x.PositionCD == ComponentPositionID);
                    PositionInDB = position.OID;
                }
                else // pozice v databazi existuje
                {
                    PositionInDB = p.OID;
                }

                Session.Part.DoPart.DivrgPositionID = PositionInDB;

                if (!Session.Part.DoPart.GetDivrg(DivergenceCode))
                {
                    Session.Part.DoPart.GetDivrg(m_divrgCode);
                    Message = Log(LogMsg.DIVERGENCE_DivergenceCodeNotExistsOrNotSpecifiedForWorkplaceGroup, DivergenceCode, m_divrgCode);
                }
                string correctImageName = UpdateImageName(imagePath);
                if (correctImageName is null)
                {
                    Log(LogMsg.AOI_WriteDiveregenceHelpMessage, partID, Session.Part.DoPart.PartID, Session.Part.DoPart.IsPanel, testComponentResult == FAIL ? "true" : "false", "OBRÁZEK SE NEPODAŘILO NAČÍST", Position, Result);
                    Message = Log(LogMsg.AOI_LogNotContainDivergenceImage, Position);
                    Session.Part.DoPart.ImageID = 0;
                }
                else
                {
                    string pathToImage = Path.Combine(pathToFolder, correctImageName);
                    Log(LogMsg.AOI_WriteDiveregenceHelpMessage, partID, Session.Part.DoPart.PartID, Session.Part.DoPart.IsPanel, testComponentResult == FAIL ? "true" : "false", pathToImage, Position, Result);
                    if (File.Exists(pathToImage) == true)
                    {
                        byte[] imageInByteArray = ImageResize(pathToImage, m_resizedImagePercents); // zmena velikosti obrazku
                        Session.Part.DoPart.ImageID = Session.Part.DoPart.Fail_AddImage(pathToImage, image: imageInByteArray);
                    }
                    else
                    {
                        Message = Log(LogMsg.AOI_DivergenceImageDoesNotExists, correctImageName);
                        Session.Part.DoPart.ImageID = 0;
                    }
                }

                if (testComponentResult == FAIL)
                {
                    Session.Part.DoPart.Fail(Result, SGConstants.DIVRG_STATUS_NOT_REPAIRED_NGAOI, partID: partID, productID: 0);
                }
                else if (testComponentResult == PASS)
                {
                    Session.Part.DoPart.Fail(Result, SGConstants.DIVRG_STATUS_FALSE_FAIL, partID: partID, productID: 0);
                }
            }
        }

        /// <summary>
        /// Metoda, ktera ke jmenu obrazku ziskaneho z logu prida suffix, ktery realny soubor opravdu ma. Vraci retezec s opravdovym jmenem souboru.
        /// </summary>
        /// <param name="imageName"> Jmeno obrazku vyparserovane z logu. </param>
        /// <returns>
        /// string  obsahujici opravdove jmeno souboru
        /// </returns>
        private string UpdateImageName(string imageName)
        {
            if (string.IsNullOrEmpty(imageName) == true)
            {
                return null;
            }

            string nameWithoutExtension = Path.GetFileNameWithoutExtension(imageName);
            string extension = Path.GetExtension(imageName);

            return string.Concat(nameWithoutExtension, m_addToImageName, extension);
        }

        /// <summary>
        /// Metoda pro vytvoreni filewatcheru ke konkretnimu souboru.
        /// </summary>
        /// <param name="path"> Cesta ke slozce, ve ktere se nachazi soubor. </param>
        /// <param name="fileName"> Nazev souboru. </param>
        private void CreateFileWatcher(string path, string fileName)
        {
            FileSystemWatcher watcher = new FileSystemWatcher();
            watcher.Path = path;
            watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
            watcher.Filter = fileName;
            watcher.Changed += new FileSystemEventHandler(OnChanged);
            watcher.EnableRaisingEvents = true;
        }

        /// <summary>
        /// Metoda pro zjisteni zmeny v souboru.
        /// </summary>
        private void OnChanged(object source, FileSystemEventArgs e)
        {
            m_changed = true;
        }

        /// <summary>
        /// Metoda pro vypocet hodnoty falesnych chyb, pokud hodnota presahne zadanou mez, tak pokud je to pozadovano tak se posle alarm.
        /// </summary>
        /// <param name="alarmVar"> Objekt, ktery obsahuje promenne pro vypocet alarmu. </param>
        public void FalseErrorAlarm(AOIAlarmVariables alarmVar)
        {
            if (alarmVar.ErrorsFalse == 0)
            {
                alarmVar.CountedValue = 0.0;
            }
            else
            {
                alarmVar.CountedValue = (Convert.ToDouble(alarmVar.ErrorsFalse) / (Convert.ToDouble(alarmVar.ErrorsFalse) + Convert.ToDouble(alarmVar.ErrorsReal))) * 100;
            }

            UpdateGUIFalseErrorAlarmVariables(alarmVar);

            if (alarmVar.CountedValue >= alarmVar.Bound)
            {
                Log(LogMsg.AOI_FalseErrorsAlarm, m_sendFalseErrorAlarm ? "byl odeslán." : "nebyl odeslán.");
                if (m_sendFalseErrorAlarm == true) // posli alarm na falesne chyby
                {
                    SendAlarm(m_falseErrorAlarmCode, Session.Job.DoJob.Barcode, Session.Job.DoProduct.Barcode, string.Join(",", Session.UserWorkGroup.LoggedUsers.Select(u => u.Name)), alarmVar.Bound, Math.Round(alarmVar.CountedValue, 2, MidpointRounding.AwayFromZero), Session.WorkPlace.Name);
                }
                alarmVar.ClearVariables();
            }
        }

        /// <summary>
        /// Metoda pro vypocet hodnoty DPMO, pokud hodnota presahne zadanou mez, tak pokud je to pozadovano tak se posle alarm.
        /// </summary>
        /// <param name="alarmVar"> Objekt, ktery obsahuje promenne pro vypocet alarmu. </param>
        public void DPMOAlarm(AOIAlarmVariables alarmVar)
        {
            if (alarmVar.Opportunities == 0)
            {
                alarmVar.CountedValue = 0.0;
            }
            else
            {
                alarmVar.CountedValue = ((Convert.ToDouble(alarmVar.ErrorsReal) / Convert.ToDouble(alarmVar.Opportunities)) * 1000000);
            }

            UpdateGUIDPMOAlarmVariables(alarmVar);

            if (alarmVar.CountedValue >= alarmVar.Bound)
            {
                Log(LogMsg.AOI_DPMOAlarm, m_sendDPMOAlarm ? "byl odeslán." : "nebyl odeslán.");
                if (m_sendDPMOAlarm == true) // posli alarm na DPMO
                {
                    SendAlarm(m_DPMOAlarmCode, Session.Job.DoJob.Barcode, Session.Job.DoProduct.Barcode, string.Join(",", Session.UserWorkGroup.LoggedUsers.Select(u => u.Name)), alarmVar.Bound, Math.Round(alarmVar.CountedValue, 2, MidpointRounding.AwayFromZero), Session.WorkPlace.Name);
                }
                alarmVar.ClearVariables();
            }
        }

        /// <summary>
        /// Metoda pro posilani alarmu.
        /// </summary>
        /// <param name="alarmCD"> Kod alarmu. </param>
        /// <param name="par"> Parametry, ktere se zobrazi v retezci alarmu. </param>
        public void SendAlarm(string alarmCD, params object[] par)
        {
            if (string.IsNullOrEmpty(alarmCD)) return;
            Session.Alarm.SendAlarm(Session.UserWorkGroup.MainUser.UserID, SGAlarmEventType.None, alarmCD, par);
        }


        /// <summary>
        /// Metoda pro ziskani vsech barcodu nachazejicich se na panelu. Vraci list obsahujici vsechny barcody nachazejici se na panelu.
        /// </summary>
        /// <returns>
        /// List<string>    list obsahujici vsechny barcody nachazejici se na panelu
        /// </returns>
        private List<string> GetAllPossibleBarcodes()
        {
            return m_aoiSakiSvc.GetAllBarcodesOnPanel(m_dxSession, Session.Part.DoPart.PartID, (ex) => Log(LogMsg.CUSTOM_WARNING_MSG, ex.Message));
        }

        /// <summary>
        /// Metoda, ktera rozhoduje zda je dil OK nebo je potreba jej zpracovat na druhem pracovisti.
        /// </summary>
        /// <returns>
        /// SGLMState   reprezentujici co se ma stat po dokonceni tohoto modulu
        /// </returns>
        private SGLMState AOICheckWorkplace()
        {
            SGLMState s = SGLMState.Handled;

            if (Session.ProcessContext.SakiData.IsBypassMode)
            {
                Session.WorkPlaceSetting.InfoData.LastPartBarcode = Session.ProcessContext.SakiData.ManageCode;
                return SGLMState.Handled | SGLMState.OK | SGLMState.UpLevel;
            }

            SakiCsvData dataFromFile = new SakiCsvData();
            dataFromFile.Clone(Session.ProcessContext.SakiData);
            Session.ProcessContext.SakiData.Clear();
            dataFromFile.IsPanel = Session.Part.DoPart.IsPanel;
            if (dataFromFile.IsPanel == true)
            {
                dataFromFile.NumOfPcbs = Session.Part.DoPart.GetPanelParts().Length;
            }
            else
            {
                dataFromFile.NumOfPcbs = 0;
            }

            if (Session.ProcessContext.SakiResultFilePathExists == true)
            {
                string pathToFile;
                if (string.IsNullOrEmpty(dataFromFile.OriginalBarcode) == true)
                {
                    pathToFile = Path.Combine(Session.ProcessContext.SakiResultFilePath, dataFromFile.ManageCode, String.Concat(dataFromFile.ManageCode, ".bzmd"));
                }
                else
                {
                    pathToFile = Path.Combine(Session.ProcessContext.SakiResultFilePath, dataFromFile.OriginalBarcode, String.Concat(dataFromFile.OriginalBarcode, ".bzmd"));
                }

                if (File.Exists(pathToFile) == true)
                {
                    SakiBzmdData bzmdFile = new SakiBzmdData();

                    if (FileHelper.TryParseFile(bzmdFile.ParseBzmdFile, pathToFile, false, Session.ProcessContext.NumOfTriesForOpenFile, Session.ProcessContext.WaitTimeBeforeAnotherTryToOpenFile, LogErrorFromTryOpenFile))
                    {
                        dataFromFile.ComponentCount = bzmdFile.ComponentCount;
                        dataFromFile.InspectedComponent = bzmdFile.InspectedComponent;
                    }
                    else
                    {
                        dataFromFile.ComponentCount = string.Empty;
                        dataFromFile.InspectedComponent = string.Empty;
                    }
                }
            }

            var opportunitiesSave = m_aoiSakiSvc.InsertOpportunitiesToDatabase(m_cDataDL, (ex) => Message = Log(LogMsg.CUSTOM_WARNING_MSG, $"Do databáze se nepodařilo vložit počet opportunities pro PartID: {Session.Part.DoPart.PartID}, JobID: {Session.Job.GetEffectiveDOJob().JobID}, JobRoutingPosIndex: {Session.CycleContext.OperationOverride.RoutingPosIndex}. Chyba {ex.Message}"), Session.Part.DoPart.PartID, Session.Job.GetEffectiveDOJob().JobID, Session.CycleContext.OperationOverride.RoutingPosIndex, Session.WorkPlace.WorkPlaceID, dataFromFile.NumberOfTests);
            if (opportunitiesSave.Item1)
            {
                Log(LogMsg.CUSTOM_INFO_MSG, $"Opportunities byly vloženy do databáze pod klíčem {opportunitiesSave.Item2}. Počet nalezených opportunities je {dataFromFile.NumberOfTests}");
            }
            else
            {
                Log(LogMsg.CUSTOM_WARNING_MSG, $"Opportunities se nepodařilo vložit do databáze. Počet nalezených opportunities je {dataFromFile.NumberOfTests}");
            }

            if (dataFromFile.AiJudge != m_TestOK)
            {
                if (dataFromFile.ParsedResultFromTestResults)
                {
                    Log(LogMsg.CUSTOM_INFO_MSG, $"Výsledek testu byl NG, ale počet chyb je 0, proto je díl vyhodnocen jako shoda.");
                    Session.Part.DoPart.SaveProcessData(dataFromFile.ToString(), string.Empty, PartDataStatus.OK);
                    s |= SGLMState.OK | SGLMState.UpLevel;
                    Session.Part.DoPart.MS = 1;
                }
                else
                {
                    Session.Part.DoPart.SaveProcessData(dataFromFile.ToString(), string.Empty, PartDataStatus.Fail);
                    Message = Log(LogMsg.AOI_EvalutePart, Session.Part.CurBarcode, dataFromFile.NumberOfTests);
                    s |= SGLMState.OK | SGLMState.UpLevel;
                    Session.Part.DoPart.MS = m_aoiNgGSStatus;
                } 
            }
            else
            {
                Session.Part.DoPart.SaveProcessData(dataFromFile.ToString(), string.Empty, PartDataStatus.OK);
                s |= SGLMState.OK | SGLMState.UpLevel;
                Session.Part.DoPart.MS = 1;
            }

            if ((string.IsNullOrEmpty(Session.FileGuard.BackupPath) == false) && (string.IsNullOrEmpty(dataFromFile.ParsedFile) == false))
            {
                if (File.Exists(dataFromFile.ParsedFile))
                {
                    string fileName = Path.GetFileName(dataFromFile.ParsedFile);
                    string backupFolder = fileName.Substring(0, 4) + "-" + fileName.Substring(4, 2); // bereme rok a měsíc z názvu souboru
                    string backupPath = Path.Combine(Session.FileGuard.BackupPath, backupFolder);
                    Message = Log(LogMsg.FILEBACKUP_TryBackupFile, dataFromFile.ParsedFile, backupPath);
                    if (!FileHelper.BackupFile(dataFromFile.ParsedFile, backupPath, Session.FileGuard.DeleteOrigin, OnBackupError))
                    {
                        Session.ProcessContext.FilesToBeBackuped.Enqueue(new Tuple<string, string>(dataFromFile.ParsedFile, backupPath));
                    }
                }
            }

            if (dataFromFile.RecordID != 0)
            {
                if (m_aoiSakiSvc.SetRecordIsProcessedState(m_cDataDL, SetError, dataFromFile.RecordID, (int)PartProcessType.ProcessedOk))
                {
                    Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Stav záznamu s ID={dataFromFile.RecordID}, byl změněn na hodnotu {(int)PartProcessType.ProcessedOk}.");
                }
                else
                {
                    Message = Log(LogMsg.CUSTOM_WARNING_MSG, $"Nepodařilo se změnit stav záznamu s ID={dataFromFile.RecordID}, zpracování souboru je přerušeno!");
                }
            }

            return s;
        }

        /// <summary>
        /// Metoda pro zjisteni nazvu zakaznika. Vraci retezec se jmenem zakaznika.
        /// </summary>
        /// <returns>
        /// string  retezec se jmenem zakaznika
        /// </returns>
        private string GetCustomer()
        {
            string customer = (from f in new XPQuery<Firm>(m_dxSession.Session)
                               where f.OID == Session.Job.DoJob.FirmID
                               select f.Name).FirstOrDefault();
            return customer;
        }

        /// <summary>
        /// Metoda, ktera ceka na dokonceni vyhodnoceni operatorem. Vraci true, pokud byl operatorem vyhodnocen a soubor ma korektni format, false pokud soubor nema korektni format nebo bylo zpracovani dilu preruseno operatorem pomoci tlacitka v GUI.
        /// </summary>
        /// <param name="dataFromFiles"> Objekt pro ulozeni vyparserovanych dat. </param>
        /// <param name="pathToFolder"> Cesta ke slozce ve ktere cekame na zmenu v souboru. </param>
        /// <param name="bfreFile"> Nazev souboru na jehoz zmenu cekame. </param>
        /// <param name="pathToBfreFile"> Kompletni cesta k souboru. </param>
        /// <returns>
        /// true    pokud ma soubor korektni strukturu
        /// false   pokud soubor nema korektni strukturu nebo zpracovani dilu bylo preruseno operatorem
        /// </returns>
        private bool WaitForFileUpdate(SakiResult dataFromFiles, string pathToFolder, string bfreFile, string pathToBfreFile)
        {
            while (dataFromFiles.BfreData.JudgementResult == "Empty")
            {
                Log(LogMsg.AOI_WaitForUpdate, dataFromFiles.BzmdData.CountedResultUnknown, Session.Part.DoPart.CurBarcode);
                CreateFileWatcher(pathToFolder, bfreFile);
                while (m_changed == false)
                {
                    if (Session.ProcessContext.AOI_Data.CancelTest == true)
                    {
                        Session.ProcessContext.AOI_Data.CancelTest = false;
                        return false;
                    }
                }
                Thread.Sleep(2000);
                dataFromFiles.BfreData.Clear();
                if (FileHelper.TryParseFile(dataFromFiles.BfreData.ParseBfreFile, pathToBfreFile, Session.ProcessContext.NumOfTriesForOpenFile, Session.ProcessContext.WaitTimeBeforeAnotherTryToOpenFile, LogErrorFromTryOpenFile) == false)
                {
                    return false;
                }
            }

            m_changed = false;
            return true;
        }

        private void SetErrorVariables(SakiResult dataFromFiles)
        {
            Session.ProcessContext.AOI_Data.ToDecide = dataFromFiles.BzmdData.CountedResultUnknown;
            Session.ProcessContext.AOI_Data.FalseErrors = dataFromFiles.BzmdData.CountedResultFalseErrors;
            Session.ProcessContext.AOI_Data.RealErrors = dataFromFiles.BzmdData.CountedResultError;
        }

        /// <summary>
        /// Metoda, ktera v GUI updatuje hodnoty promennych pro alarm falesnych chyb.
        /// </summary>
        /// <param name="feVars"> Objekt obsahujici promenne alarmu pro falsene chyby. </param>
        public void UpdateGUIFalseErrorAlarmVariables(AOIAlarmVariables feVars)
        {
            Session.ProcessContext.AOI_Data.CountedFalseErrorsFE = feVars.ErrorsFalse;
            Session.ProcessContext.AOI_Data.CountedRealErrorsFE = feVars.ErrorsReal;
            Session.ProcessContext.AOI_Data.FalseErrorAlarm = feVars.CountedValue;
        }

        /// <summary>
        /// Metoda, ktera v GUI updatuje hodnoty promennych pro alarm DPMO.
        /// </summary>
        /// <param name="dpmoVars"> Objekt obsahujici promenne alarmu pro DPMO. </param>
        public void UpdateGUIDPMOAlarmVariables(AOIAlarmVariables dpmoVars)
        {
            Session.ProcessContext.AOI_Data.CountedOpportunitiesDPMO = dpmoVars.Opportunities;
            Session.ProcessContext.AOI_Data.CountedRealErrorsDPMO = dpmoVars.ErrorsReal;
            Session.ProcessContext.AOI_Data.DpmoAlarm = dpmoVars.CountedValue;
        }

        /// <summary>
        /// Metoda pro uložení obrázku celého panelu, která podle parametru modulu určuje zda je požadované obrázek uložit. 
        /// </summary>
        /// <param name="isPanelOk"> Určení zda se na panelu nachází nějaká opravdová chyba nebo jsou všechny falešné. </param>
        /// <param name="pathToFolder"> Cesta ke složce ve které se nachází obrázek celého panelu. </param>
        private void SaveImageOfPanel(bool isPanelOk, string pathToFolder)
        {
            switch (m_savePanelImage)
            {
                case SavePanelImage.Error:
                    if ((isPanelOk == false) && (string.IsNullOrEmpty(m_panelPhotoFileName) == false))
                    {
                        Session.Part.DoPart.AddImage(File.ReadAllBytes(Path.Combine(pathToFolder, m_panelPhotoFileName)));
                    }
                    break;
                case SavePanelImage.FalseError:
                    if (string.IsNullOrEmpty(m_panelPhotoFileName) == false)
                    {
                        Session.Part.DoPart.AddImage(File.ReadAllBytes(Path.Combine(pathToFolder, m_panelPhotoFileName)));
                    }
                    break;
                case SavePanelImage.NotSave:
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Metoda, ktera provede zmenu velikosti obrazku na X% puvodniho podle hodnoty X ziskane v OnSetParameters. Navratova hodnota je bytove pole reprezentujici zmenseny obrazek, pokud je zmenseni uspesne, nebo null pokud se zmenseni nepodarilo.
        /// </summary>
        /// <param name="path"> Cesta k souboru. </param>
        /// <param name="percents"> Na jakou procentualni velikost se ma obrazek zmensit. </param>
        /// <returns>
        /// bytove pole     reprezentujici zmenseny obrazek, pokud zmenseni dopadlo uspesne
        /// null            pokud nastala chyba
        /// </returns>
        public byte[] ImageResize(string path, int percents)
        {
            string extension = Path.GetExtension(path);
            try
            {
                // zmenseni obrazku na zadane procento
                int newWidth = 0;
                int newHeight = 0;
                using (var input = new Bitmap(path))
                {
                    int originalWidth = input.Width;
                    int originalHeight = input.Height;
                    newWidth = (int)(originalWidth * percents) / 100;
                    newHeight = (int)(originalHeight * percents) / 100;
                }

                using (var result = new Bitmap(newWidth, newHeight))
                {
                    using (var input = new Bitmap(path))
                    {
                        using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
                        {
                            g.DrawImage(input, 0, 0, newWidth, newHeight);
                        }
                    }

                    // vytvoreni bytoveho pole ze zmenseneho obrazku
                    using (var ms = new MemoryStream())
                    {
                        switch (extension.ToLower())
                        {
                            case ".jpeg":
                            case ".jpg":
                                result.Save(ms, ImageFormat.Jpeg);
                                break;
                            case ".bmp":
                                result.Save(ms, ImageFormat.Bmp);
                                break;
                            case ".gif":
                                result.Save(ms, ImageFormat.Gif);
                                break;
                            case ".png":
                                result.Save(ms, ImageFormat.Png);
                                break;
                            default:
                                return null;
                        }
                        return ms.ToArray();
                    }
                }
            }
            catch { }
            return null;
        }

        private void OnBackupError(Exception e) => Message = Log(LogMsg.FILE_FileBackupNotOk, e.Message);

        private List<Tuple<string, bool>> PrepareBarcodes(List<string> barcodes)
        {
            List<Tuple<string, bool>> retVal = new List<Tuple<string, bool>>();
            string tempSN = string.Empty;
            string tempSNWithDigitRegex = string.Empty;
            for (int i = 0; i < barcodes.Count; i++)
            {
                if (Session.ProcessContext.EscapeHelper.IsSerialNumberNormalizedRegex(barcodes[i], ref tempSN))
                {
                    barcodes[i] = tempSN;
                    retVal.Add(new Tuple<string, bool>(tempSN, true));
                }
                else
                {
                    retVal.Add(new Tuple<string, bool>(barcodes[i], false));
                }
            }
            return retVal;
        }

        private Tuple<string, bool> FindCorrectBarcode(List<Tuple<string, bool>> barcodes, List<string> directories)
        {
            foreach (var barcode in barcodes)
            {
                foreach (var directory in directories)
                {
                    Regex barcodeRegex = new Regex(barcode.Item1);
                    if (barcodeRegex.IsMatch(directory))
                    {
                        return barcode;
                    }
                }
            }
            return null;
        }

        private void LogErrorFromTryOpenFile(string msg) => Message = Log(LogMsg.CUSTOM_WARNING_MSG, msg);

        private void InitXPO(MSSQL cdata)
        {
            var cs = "XpoProvider=MSSqlServer;" + cdata.ConnectionString;

            var dict = new ReflectionDictionary();
            dict.GetDataStoreSchema(typeof(LaserBoard));
            dict.GetDataStoreSchema(typeof(LaserBoardCode));
            dict.GetDataStoreSchema(typeof(ParsedFile));

            var provider = XpoDefault.GetConnectionProvider(cs, AutoCreateOption.SchemaAlreadyExists);
            m_cDataDL = new ThreadSafeDataLayer(dict, provider);
        }
        #endregion
    }

    public enum SavePanelImage
    {
        Error,
        FalseError,
        NotSave,
    }
}