AOI_Saki_RackSaveProcessData.cs

*AOI_Saki_RackSaveProcessData.cs*

using Bartech.SGCore.Base;
using Bartech.SGCore.Base.Web;
using Bartech.SGCore.Local;
using Bartech.SGCore.Local.DataObjects;
using Bartech.SGCore.Logic.NVCZ.Modules.AOI.Model;
using Bartech.SGCore.Model;
using DevExpress.Xpo;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
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_SavePcbData", "Modul pro ukladani procesnich dat pro jednotliva Pcb z AOI Saki", ModuleGroupType.External)]
    public class AOI_Saki_RackSaveProcessData : SGModuleLocal
    {
        #region Constructor
        public AOI_Saki_RackSaveProcessData(SGSession session)
            : base(session)
        {
            Session.Initialized += new EventHandler((s, e) =>
            {
                m_dxSession = Session.Resolve<SGDxSessionLocker>();
                m_rack = new DORack(this);
                m_dataFromFiles = new SakiResult();
            });
        }
        #endregion

        #region Private variables
        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_addToImageName = "_Color";
        private string m_ngTestFilePath = string.Empty;
        private bool m_changed = false;
        private int m_aoiNgGSStatus = 35;
        private long m_lastJobID;
        private BusOperation m_lastOperationOverride;

        string m_bfreFile = string.Empty;
        string m_bzmdFile = string.Empty;
        string m_pathToFolder = string.Empty;
        string m_pathToBfreFile = string.Empty;
        string m_pathToBzmdFile = string.Empty;
        SakiResult m_dataFromFiles;

        private DORack m_rack;

        private RackAction m_rackAction = RackAction.ClearParts;
        private StateOfModule m_moduleState = StateOfModule.ok;
        private string m_customer = string.Empty;
        private string m_programName = string.Empty;
        private string m_tempPathToFolder = string.Empty;

        private List<string> m_directories = null;
        private string m_directoryRegexString = string.Empty;
        private Regex m_directoryRegex = null;
        private DateTime m_lastModification;
        private string m_correctFolder = string.Empty;
        private Regex m_numOfTestRegex = new Regex(@".*\((?<NumOfTest>[0-9]+)\)");
        #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_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_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_rackAction = Session.SysParams.GetEnum(this, "FrameAction", m_rackAction, "Mají se díly odstranit z rámečku? Ano - ClearParts, Ne - NotClearParts");
            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_divrgCode = Session.SysParams.TryGetString(this, "DivrgBarcode", m_divrgCode, "Barkód chyby, která se zapíše v případě chyby.");

            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); }

            return base.OnSetParameters();
        }

        public override void StartLevel(SGTreeModuleData mdata)
        {
            if ((Session.Job.GetEffectiveDOJob().JobID != m_lastJobID) || (Session.CycleContext.OperationOverride != m_lastOperationOverride))
            {
                m_moduleState = StateOfModule.ok;
                m_lastJobID = Session.Job.GetEffectiveDOJob().JobID;
                m_lastOperationOverride = Session.CycleContext.OperationOverride;
                m_customer = GetCustomer();
                if (string.IsNullOrEmpty(m_customer) == true)
                {
                    Message = Log(LogMsg.AOISAKI_CustomerIsEmpty);
                    return;
                }
                m_programName = Session.Job.DoJob.DoProduct.GmsKatalogParams["Hlavní program"];
                if (string.IsNullOrEmpty(m_programName) == true)
                {
                    Message = Log(LogMsg.AOI_ProgramNameIsNotSet);
                    m_moduleState = StateOfModule.programNameNotDefined;
                    return;
                }
                m_tempPathToFolder = Path.Combine(m_ngTestFilePath, m_customer, m_programName, "Lot");
                if (Directory.Exists(m_tempPathToFolder) == false)
                {
                    Message = Log(LogMsg.AOISAKI_DirectoryWithResultDoesNotExists, m_tempPathToFolder);
                    m_moduleState = StateOfModule.folderWithResultsDoesNotExists;
                    return;
                }
            }
            m_rack.Clear();
            base.StartLevel(mdata);
        }

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

            if (mdata.LevelState == SGLevelState.Echo)
            {
                switch (m_moduleState)
                {
                    case StateOfModule.ok:
                        Message = Log(LogMsg.RACK_WaitingForRack);
                        break;
                    case StateOfModule.folderWithResultsDoesNotExists:
                        Message = Log(LogMsg.AOISAKI_DirectoryWithResultDoesNotExists, m_tempPathToFolder);
                        break;
                    case StateOfModule.programNameNotDefined:
                        Message = Log(LogMsg.AOI_ProgramNameIsNotSet);
                        break;
                    case StateOfModule.customerNameNotDefined:
                        Message = Log(LogMsg.AOISAKI_CustomerIsEmpty);
                        break;
                    default:
                        break;
                }
                s = SGLMState.Handled;
            }
            else if ((mdata.LevelState == SGLevelState.WaitingForData) && (m_moduleState == StateOfModule.ok))
            {
                if (Session.Data.CheckData(DataType.Rack, data))
                {
                    m_rack.Load(data.Text, true);
                    if (m_rack.IsEmpty)
                    {
                        Message = Log(LogMsg.RACK_IsEmpty);
                        m_rack.Clear();
                        return SGLMState.Handled | SGLMState.Fail | SGLMState.ResetIndex;
                    }

                    m_directories = Directory.GetDirectories(m_tempPathToFolder).ToList();
                    m_directoryRegexString = m_rack.Barcode + ".*";
                    m_directoryRegex = new Regex(m_directoryRegexString);
                    m_lastModification = DateTime.MinValue;
                    m_correctFolder = string.Empty;

                    foreach (string dir in m_directories)
                    {
                        if (m_directoryRegex.IsMatch(dir) == true)
                        {
                            DirectoryInfo di = new DirectoryInfo(dir);
                            DateTime lastWrite = di.LastWriteTime;
                            if (lastWrite > m_lastModification)
                            {
                                m_lastModification = lastWrite;
                                m_correctFolder = dir;
                            }
                        }
                    }

                    if (string.IsNullOrEmpty(m_correctFolder) == true)
                    {
                        Message = Log(LogMsg.DIRECTORY_NotFindedByRegex, m_directoryRegexString);
                        return SGLMState.Handled | SGLMState.Fail | SGLMState.ResetIndex;
                    }

                    string correctBarcode = m_rack.Barcode;
                    if (m_numOfTestRegex.IsMatch(m_correctFolder))
                    {
                        correctBarcode = correctBarcode + "(" + m_numOfTestRegex.Match(m_correctFolder).Groups["NumOfTest"].Value + ")";
                    }

                    // sestaveni spravne cesty k souborum se spravnymi nazvy
                    m_bfreFile = string.Concat("DATA_", correctBarcode, ".bfre");
                    m_bzmdFile = string.Concat(correctBarcode, ".bzmd");
                    m_pathToFolder = Path.Combine(m_ngTestFilePath, m_customer, m_programName, Path.Combine("Lot", m_correctFolder));
                    m_pathToBfreFile = Path.Combine(m_pathToFolder, m_bfreFile);
                    m_pathToBzmdFile = Path.Combine(m_pathToFolder, m_bzmdFile);

                    Log(LogMsg.CUSTOM_INFO_MSG, $"Zkouším hledat soubor {m_pathToBfreFile}");
                    if (!File.Exists(m_pathToBfreFile))
                    {
                        m_bfreFile = string.Concat("DATA_", m_rack.Barcode, ".bfre");
                        m_pathToBfreFile = Path.Combine(m_pathToFolder, m_bfreFile);
                        Log(LogMsg.CUSTOM_INFO_MSG, $"Zkouším hledat soubor {m_pathToBfreFile}");
                        if (!File.Exists(m_pathToBfreFile))
                        {
                            Message = Log(LogMsg.CUSTOM_WARNING_MSG, $"Nepodařilo se najít soubor typu bfre");
                            return SGLMState.Handled | SGLMState.Fail;
                        }
                    }

                    Log(LogMsg.CUSTOM_INFO_MSG, $"Zkouším hledat soubor {m_pathToBzmdFile}");
                    if (!File.Exists(m_pathToBzmdFile))
                    {
                        m_bzmdFile = string.Concat(m_rack.Barcode, ".bzmd");
                        m_pathToBzmdFile = Path.Combine(m_pathToFolder, m_bzmdFile);
                        Log(LogMsg.CUSTOM_INFO_MSG, $"Zkouším hledat soubor {m_pathToBzmdFile}");
                        if (!File.Exists(m_pathToBzmdFile))
                        {
                            Message = Log(LogMsg.CUSTOM_WARNING_MSG, $"Nepodařilo se najít soubor typu bzmd");
                            return SGLMState.Handled | SGLMState.Fail;
                        }
                    }

                    m_dataFromFiles.Clear();

                    if (m_dataFromFiles.BfreData.ParseBfreFile(m_pathToBfreFile).Item1 == false) // pro zjisteni zda jiz nebylo rozhodnuto
                    {
                        Session.Part.Clear();
                        Message = Log(LogMsg.FILE_ERROR, m_pathToBfreFile);
                        return SGLMState.Handled | SGLMState.Fail | SGLMState.ResetIndex;
                    }

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

                    if (m_dataFromFiles.BzmdData.ParseBzmdFile(m_pathToBzmdFile, true).Item1 == false) // pro vypsani udaju kolik je potrebnych rozhodnuti operatora
                    {
                        s |= SGLMState.ResetLevel;
                        Message = Log(LogMsg.FILE_ERROR, m_pathToBfreFile);
                        Session.Part.Clear();
                        return SGLMState.Handled | SGLMState.Fail | SGLMState.ResetIndex;
                    }

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

                    // ziskani aktualizovanych udaju o povaze chyb po rozhodnuti operatora
                    m_dataFromFiles.BzmdData.Clear();
                    m_dataFromFiles.BzmdData.ParseBzmdFile(m_pathToBzmdFile, true);
                    Log(LogMsg.FILE_ReadFile, m_pathToBzmdFile, "bzmd");

                    SGPartItem[] partsFromRack = m_rack.LoadParts(m_rack.RackID);
                    m_rack.RemoveAllParts();


                    for (int i = 0; i < partsFromRack.Length; i++)
                    {
                        Log(LogMsg.CUSTOM_INFO_MSG, $"index dilu: {i}, OID dilu: {partsFromRack[i].ID}, barcode dilu: {partsFromRack[i].Barcode}");
                    }

                    List<IGrouping<string, SakiTestResult>> resultsGroupedBySubReference = m_dataFromFiles.BzmdData.TestedPos.GroupBy(x => x.SubReference).ToList();
                    foreach (var item in resultsGroupedBySubReference)
                    {
                        Log(LogMsg.CUSTOM_INFO_MSG, $"nalezena subreference potencialne ng dilu: {item.Key}, prepocitana subreference: {Int32.Parse(item.Key) - 1}");
                    }
                    m_dataFromFiles.BzmdData.TestResults.AddRange(resultsGroupedBySubReference);
                    if (resultsGroupedBySubReference.Count != partsFromRack.Length) // v racku/ramecku mame vsechny dily
                    {
                        AllPartsInFrame(resultsGroupedBySubReference, partsFromRack);
                    }
                    else // rack/ramecek obsahuje pouze NOK dily z prvniho pracoviste
                    {
                        OnlyNgPartsInFrame(resultsGroupedBySubReference, partsFromRack);
                    }

                    if (m_rackAction == RackAction.NotClearParts)
                    {
                        foreach (var item in partsFromRack)
                        {
                            m_rack.AddPart(item.ID);
                        }
                    }
                    m_rack.Clear();
                    m_dataFromFiles.BzmdData.TestResults.Clear();
                    m_dataFromFiles.BzmdData.Clear();
                    m_dataFromFiles.BfreData.Clear();
                    s = SGLMState.Handled | SGLMState.OK | SGLMState.UpLevel;
                }
            }
            return s;
        }

        public override void Clear()
        {
            m_lastJobID = 0;
            if (m_lastOperationOverride != null)
            {
                m_lastOperationOverride.Clear();
            }
            m_customer = string.Empty;
            m_programName = string.Empty;
            m_tempPathToFolder = string.Empty;
            if (m_rack != null)
            {
                m_rack.Clear();
            }

            base.Clear();
        }
        #endregion

        #region Helper functions
        private string GetCustomer()
        {
            string customer;
            using (m_dxSession.GetLock())
            {
                customer = (from f in new XPQuery<Firm>(m_dxSession.Session)
                            where f.OID == Session.Job.DoJob.FirmID
                            select f.Name).FirstOrDefault();
            }
            return customer;
        }

        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 (dataFromFiles.BfreData.ParseBfreFile(pathToBfreFile).Item1 == false)
                {
                    return false;
                }
            }

            m_changed = false;
            return true;
        }

        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;
        }

        private void OnChanged(object source, FileSystemEventArgs e)
        {
            m_changed = true;
        }

        /// <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, m_aoiNgGSStatus == 35 ? SGConstants.DIVRG_STATUS_NOT_REPAIRED_NGAOI : SGConstants.DIVRG_STATUS_NOT_REPAIRED, 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 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;
        }

        /// <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);
        }

        private void AllPartsInFrame(List<IGrouping<string, SakiTestResult>> resultsGroupedBySubReference, SGPartItem[] partsFromRack)
        {
            Log(LogMsg.CUSTOM_INFO_MSG, $"Varianta AllInFrame, {resultsGroupedBySubReference.Count()} : {partsFromRack.Length}");
            List<int> ngSubref = new List<int>();
            try
            {
                Session.Part.ParallelObjectIndex = 1;
                for (int i = 0; i < resultsGroupedBySubReference.Count(); i++)
                {
                    int index = Int32.Parse(resultsGroupedBySubReference[i].Key) - 1;
                    var procesedPart = partsFromRack[index];

                    ngSubref.Add(index);
                    Session.Part.DoPart.Clear();
                    Session.Part.DoPart.PartID = procesedPart.ID;
                    Session.Part.DoPart.JobID = Session.Job.DoJob.JobID;
                    Session.Part.DoPart.ProductID = Session.Job.DoProduct.ProductID;
                    Session.Part.DoPart.WorkModeID = Session.WorkModeManager.ActualWorkMode;
                    Session.Part.DoPart.LogGroupID = Session.Part.DoPart.LogGroupID;
                    Log(LogMsg.CUSTOM_INFO_MSG, $"Vybran dil na indexu: {index}, OID dilu: {procesedPart.ID}");
                    if (Session.Part.CheckPartPartID(true) == true)
                    {
                        bool isPartOk = true;
                        foreach (var oneTestItem in resultsGroupedBySubReference[i])
                        {
                            WriteDivergence(oneTestItem.RefName, oneTestItem.ErrorType, oneTestItem.Bitmap, m_pathToFolder, oneTestItem.Result, procesedPart.ID);
                            if (oneTestItem.Result == FAIL)
                            {
                                isPartOk = false;
                            }
                        }

                        if (isPartOk == true)
                        {
                            Session.Part.DoPart.SaveProcessData(m_dataFromFiles.BzmdData.CreateProcessData(Int32.Parse(resultsGroupedBySubReference[i].Key), "Ok", procesedPart.Barcode), string.Empty, PartDataStatus.OK);
                            Session.Part.DoPart.MS = 1;
                        }
                        else
                        {
                            Session.Part.DoPart.SaveProcessData(m_dataFromFiles.BzmdData.CreateProcessData(Int32.Parse(resultsGroupedBySubReference[i].Key), "Ng", procesedPart.Barcode), string.Empty, PartDataStatus.Fail);
                            Session.Part.DoPart.MS = m_aoiNgGSStatus;
                        }

                        Session.Part.DoPart.End();
                    }
                }

                // doplneni operace rozhodovaciho terminalu pro OK kusy

                //var okSubref = Enumerable.Range(0, partsFromRack.Length).Except(ngSubref);

                //Log(LogMsg.CUSTOM_INFO_MSG, $"doplneni pruchodu OK dilum jiz od stroje na nasledujicich indexech: {string.Join(",", okSubref)}");
                //foreach (var item in okSubref)
                //{
                //    var procesedPart = partsFromRack[item];
                //    Session.Part.DoPart.Clear();
                //    Session.Part.DoPart.PartID = procesedPart.ID;
                //    Session.Part.DoPart.JobID = Session.Job.DoJob.JobID;
                //    Session.Part.DoPart.ProductID = Session.Job.DoProduct.ProductID;
                //    Session.Part.DoPart.WorkModeID = Session.WorkModeManager.ActualWorkMode;
                //    Session.Part.DoPart.LogGroupID = Session.Part.DoPart.LogGroupID;
                //    Log(LogMsg.CUSTOM_INFO_MSG, $"Vybran dil na indexu: {item}, OID dilu: {procesedPart.ID}");
                //    if (Session.Part.CheckPartPartID(true) == true)
                //    {
                //        Session.Part.DoPart.SaveProcessData("Vyhodnocen OK strojem.", string.Empty, PartDataStatus.OK);
                //        Session.Part.DoPart.MS = 1;
                //        Session.Part.DoPart.End();
                //    }
                //}

            }
            finally
            {
                Session.Part.Clear();
                Session.Part.ParallelObjectIndex = 0;
            }
        }


        private void OnlyNgPartsInFrame(List<IGrouping<string, SakiTestResult>> resultsGroupedBySubReference, SGPartItem[] partsFromRack)
        {
            Log(LogMsg.CUSTOM_INFO_MSG, $"Varianta OnlyNgPartsInFrame, {resultsGroupedBySubReference.Count()} : {partsFromRack.Length}");
            try
            {
                Session.Part.ParallelObjectIndex = 1;
                int indexer = 0;
                for (int i = 0; i < resultsGroupedBySubReference.Count(); i++)
                {
                    var procesedPart = partsFromRack[indexer];
                    Session.Part.DoPart.Clear();
                    Session.Part.DoPart.PartID = procesedPart.ID;
                    Session.Part.DoPart.JobID = Session.Job.DoJob.JobID;
                    Session.Part.DoPart.ProductID = Session.Job.DoProduct.ProductID;
                    Session.Part.DoPart.WorkModeID = Session.WorkModeManager.ActualWorkMode;
                    Session.Part.DoPart.LogGroupID = Session.Part.DoPart.LogGroupID;
                    Log(LogMsg.CUSTOM_INFO_MSG, $"Vybran dil na indexu: {indexer}, OID dilu: {procesedPart.ID}");
                    if (Session.Part.CheckPartPartID(true) == true)
                    {
                        bool isPartOk = true;
                        foreach (var oneTestItem in resultsGroupedBySubReference[i])
                        {
                            WriteDivergence(oneTestItem.RefName, oneTestItem.ErrorType, oneTestItem.Bitmap, m_pathToFolder, oneTestItem.Result, procesedPart.ID);
                            if (oneTestItem.Result == FAIL)
                            {
                                isPartOk = false;
                            }
                        }

                        if (isPartOk == true)
                        {
                            Session.Part.DoPart.SaveProcessData(m_dataFromFiles.BzmdData.CreateProcessData(Int32.Parse(resultsGroupedBySubReference[i].Key), "Ok", procesedPart.Barcode), string.Empty, PartDataStatus.OK);
                            Session.Part.DoPart.MS = 1;
                        }
                        else
                        {
                            Session.Part.DoPart.SaveProcessData(m_dataFromFiles.BzmdData.CreateProcessData(Int32.Parse(resultsGroupedBySubReference[i].Key), "Ng", procesedPart.Barcode), string.Empty, PartDataStatus.Fail);
                            Session.Part.DoPart.MS = m_aoiNgGSStatus;
                        }

                        Session.Part.DoPart.End();
                    }

                    indexer++;
                }
            }
            finally
            {
                Session.Part.Clear();
                Session.Part.ParallelObjectIndex = 0;
            }
        }
        #endregion
    }

    #region Enum
    public enum RackAction
    {
        ClearParts,
        NotClearParts,
    }

    public enum StateOfModule
    {
        ok,
        folderWithResultsDoesNotExists,
        programNameNotDefined,
        customerNameNotDefined,
    }
    #endregion
}