AOI_Saki_RackParser.cs

*AOI_Saki_RackParser.cs*

using Bartech.SGCore.Base;
using Bartech.SGCore.Base.Web;
using Bartech.SGCore.Helpers;
using Bartech.SGCore.Local;
using Bartech.SGCore.Local.DataObjects;
using Bartech.SGCore.Logic.NVCZ.Modules.AOI.Model;
using Bartech.SGCore.Model;
using Bartech.SGCore.Model.ContextData;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using PartDataStatus = Bartech.SGCore.Local.DataObjects.PartDataStatus;

namespace Bartech.SGCore.Logic.NVCZ.Modules.AOI
{
    [XModule("NVCZ_AOI_Saki_Pcb_Parser", "Modul pro zpracování jednotlivých pcb na AOI Saki pomocí nástroje.", ModuleGroupType.External)]
    public class AOI_Saki_RackParser : SGPartStart
    {
        #region Constructor
        public AOI_Saki_RackParser(SGSession session)
        : base(session)
        {
            m_checkedPars = new List<DOPart>();
            m_prevCheckedPars = new List<long>();
            m_dxSession = Session.Resolve<SGDxSessionLocker>();
            AllowedInDevs = new SGDeviceType[] { SGDeviceType.Scanner, SGDeviceType.FileSystem };
            Session.Initialized += delegate (object s, EventArgs e)
            {
                if (Session.IsStudioSession) return;
                m_checkedPars.Clear();
                m_rack = new DORack(this);
                m_testAoi = new SakiCsvData();
            };
        }
        #endregion

        #region Private variables
        private SGDxSessionLocker m_dxSession;
        private int m_frameCapacity { get; set; }
        private List<DOPart> m_checkedPars;
        private List<long> m_prevCheckedPars;
        private BusOperation m_lastOperationOverride;
        private long m_lastJobID;
        private int m_masterCommand = 1;
        private int m_endLoadingPartsCommand = 400;
        private int m_removePartCommand = 401;
        private Bartech.SGCore.Logic.NVCZ.Modules.AOI.Model.SakiCsvData m_testAoi;
        private bool m_waitForFile = false;
        private DORack m_rack;
        private WhatHappenToParts m_partInRackAction = WhatHappenToParts.onlyNgInFrame;
        private int m_ngMSStatus = 35;
        private bool m_allowRetestImmediately = true;
        #endregion

        #region Module overrides
        protected override bool OnSetParameters()
        {
            m_partInRackAction = Session.SysParams.GetEnum(this, "PartInFrameAction", m_partInRackAction, string.Format("Které díly mají zístat v rámečku po dokončení testu. Hodnoty: {0}", string.Join(", ", Enum.GetNames(typeof(WhatHappenToParts)))));
            m_ngMSStatus = Session.SysParams.GetInt32(this, "AoiNgGSStatus", m_ngMSStatus, "Hodnota udávající jaký status se nastaví dílu při NOT OK testu dílu na pracovišti.");
            m_allowRetestImmediately = Session.SysParams.GetBoolean(this, "AllowRetestImmediately", m_allowRetestImmediately, "Pokud je true tak je možné načíst stejné sériová čísla hned v dalším pracovním cyklu, jinak je nutné provést cyklus s jinými díly.");
            LogResult();

            return base.OnSetParameters();
        }

        public override void StartLevel(SGTreeModuleData mdata)
        {
            if (Session.CycleContext.Tools.Count() == 1)
            {
                var toolKat = new DOKatalogParams(this, "Tool");
                toolKat.Load(Session.CycleContext.Tools[0].OID);
                m_frameCapacity = toolKat.GetValue("Kapacita", 1, false);
            }
            else
            {
                m_frameCapacity = 0;
            }

            if ((Session.Job.GetEffectiveDOJob().JobID != m_lastJobID) || (Session.CycleContext.OperationOverride != m_lastOperationOverride))
            {
                m_lastJobID = Session.Job.GetEffectiveDOJob().JobID;
                m_lastOperationOverride = Session.CycleContext.OperationOverride;
                m_checkedPars.Clear();
                m_prevCheckedPars.Clear();
                m_rack.Clear();
            }
            base.StartLevel(mdata);
        }

        public override SGLMState HandlePortData(SGTreeModuleData mdata, SGReceivedData data)
        {
            SGLMState s = SGLMState.NotHandled;
            if (mdata.LevelState == SGLevelState.Echo)
            {
                if (m_waitForFile == true)
                {
                    Log(LogMsg.FILE_WaitForFile);
                }
                else if (m_frameCapacity == 0)
                {
                    Message = Log(LogMsg.PART_WaitSN);
                }
                else
                {
                    Message = Log(LogMsg.PV_WaitFillFrame, Session.CycleContext.Tools[0].ToolCD, m_checkedPars.Count(), m_frameCapacity);
                }
                s = SGLMState.Handled;
            }
            else if ((mdata.LevelState == SGLevelState.WaitingForData) && (data.DeviceType == SGDeviceType.Scanner) && (m_waitForFile == false)) // nacitame dily
            {
                if (Session.Data.CheckData(DataType.Command, data) && Session.SysCommands.IsSysCommand(this, data, (SGSysCommandType)(int)m_endLoadingPartsCommand)) // command na ukonceni nacitani
                {
                    if (m_checkedPars.Count > 0)
                    {
                        m_waitForFile = true;
                        s = SGLMState.Handled | SGLMState.OK;
                    }
                    else
                    {
                        Log(LogMsg.FRAME_Empty, Session.CycleContext.Tools[0].ToolCD);
                        s = SGLMState.Handled | SGLMState.OK;
                    }
                }
                else if (Session.Data.CheckData(DataType.Command, data) && Session.SysCommands.IsSysCommand(this, data, (SGSysCommandType)(int)m_removePartCommand)) // command na odebrani dilu
                {
                    if (m_checkedPars.Count > 0)
                    {
                        int indexOfPartToRemove = m_checkedPars.Count() - 1;
                        Log(LogMsg.FRAME_PartRemove, m_checkedPars[indexOfPartToRemove].CurBarcode, Session.CycleContext.Tools[0].ToolCD);
                        m_checkedPars.RemoveAt(indexOfPartToRemove);
                    }
                    else
                    {
                        Log(LogMsg.FRAME_CantRemovePart, Session.CycleContext.Tools[0].ToolCD);
                    }
                    s = SGLMState.Handled | SGLMState.OK;
                }
                else if (Session.Data.CheckData(DataType.Command, data) && Session.SysCommands.IsSysCommand(this, data, (SGSysCommandType)(int)m_masterCommand)) // neskutečný ojeb pro funkčnost commandu C0001 
                {
                    m_lastJobID = 0;
                    if (m_lastOperationOverride != null)
                    {
                        m_lastOperationOverride.Clear();
                    }
                }
                else 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;
                    }
                    m_rack.RemoveAllParts();
                    m_rack.Clear();
                }
                else if (Session.Data.CheckData(DataType.SerialNo, data)) // prislo SN
                {
                    if (!Session.Data.CheckData(DataType.Job, data.Text)) // zakaznicky kod jobu
                    {
                        string dataText = data.Text;
                        DOJob j = new DOJob(this);
                        if (j.LoadByCustomerJob(dataText, false))
                        {
                            dataText = j.Barcode;
                            Session.SetNextData(Session.Data.CreateReceivedData(SGDeviceType.Scanner, dataText, true));
                            return SGLMState.Handled | SGLMState.OK;
                        }
                    }
                    if (IsPartInQueue(data.Text) == true) // kontrola zda neprislo sn ktere je jiz v ramecku
                    {
                        Log(LogMsg.PV_PartWasAlreadyScanned, data.Text);
                        s = SGLMState.Handled | SGLMState.Fail;
                    }
                    else if (m_checkedPars.Count < m_frameCapacity) // SN neni v ramecku a jeste se tam vleze
                    {
                        s = base.HandlePortData(mdata, data);

                        if (Session.Part.DoPart.IsPanel)
                        {
                            Message = Log(LogMsg.AOISAKI_BadScript, data.Text);
                            s = SGLMState.Handled | SGLMState.Fail;
                        }

                        if (Session.Part.DoPart.IsPartChecked)
                        {
                            var dop = new DOPart(this);
                            Session.Part.DoPart.Clone(dop);
                            m_checkedPars.Add(dop);
                            Log(LogMsg.FRAME_AddPartToFrame, dop.CurBarcode, Session.CycleContext.Tools[0].ToolCD);
                            Session.Part.Clear();
                            if (m_checkedPars.Count == m_frameCapacity || m_frameCapacity == 0)
                            {
                                m_waitForFile = true;
                                return SGLMState.Handled | SGLMState.OK;
                            }
                            else
                            {
                                s = SGLMState.Handled | SGLMState.OK;
                            }
                        }
                    }
                    else // SN neni v ramecku a ten je plny => nemelo by se stat
                    {
                        Message = LogResult();
                        s = SGLMState.Handled | SGLMState.Fail;
                    }
                }
            }
            else if ((mdata.LevelState == SGLevelState.WaitingForData) && (data.DeviceType == SGDeviceType.FileSystem) && (m_waitForFile == true)) // mame nactene dily a cekame na soubor
            {
                m_testAoi.Clear();
                if (m_testAoi.ParseCsvFile(data.Text).Item1 == false)
                {
                    Message = Log(LogMsg.FILE_ERROR, data.Text);
                    return SGLMState.Handled | SGLMState.Fail | SGLMState.ResetLevel;
                }
                if (m_testAoi.SubboardResults.Count() != m_checkedPars.Count())
                {
                    Message = Log(LogMsg.AOISAKI_BadPcbCount);
                    return SGLMState.Handled | SGLMState.Fail | SGLMState.ResetLevel;
                }
                if (m_rack.Load(m_testAoi.ManageCode, false) == false)
                {
                    Message = Log(LogMsg.RACK_FailedToLoad, m_testAoi.ManageCode);
                    return SGLMState.Handled | SGLMState.Fail | SGLMState.ResetLevel;
                }
                if (m_rack.Qty > 0)
                {
                    Message = Log(LogMsg.AOISAKI_NotEmptyRack);
                    m_rack.RemoveAllParts();
                }
                m_waitForFile = false;
                SaveParts();

                if (string.IsNullOrEmpty(Session.FileGuard?.BackupPath) == false)
                {
                    if (File.Exists(data.Text))
                    {
                        string fileName = Path.GetFileName(data.Text);
                        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, data.Text, backupPath);
                        FileHelper.BackupFile(data.Text, backupPath, Session.FileGuard.DeleteOrigin, OnBackupError);
                    }
                }

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

        public override void Clear()
        {
            base.Clear();
        }
        #endregion

        #region Helper functions
        private bool IsPartInQueue(string sn)
        {
            // over, jestli dil existuje
            var p = new DOPart(this);
            if (!p.PartExistsCheck(sn)) return false;
            // pokud je v aktualnim seznamu, tak se jedna o duplicitni nacteni
            if (m_checkedPars.Any(x => x.PartID == p.PartID)) return true;
            // pokud je v predeslem seznamu, tak se jedna take o duplicitni nacteni
            if (m_prevCheckedPars.Any(x => x == p.PartID)) return true;
            return false;
        }

        private void SaveParts()
        {
            (m_checkedPars.FirstOrDefault(x => x.MustShiftRouting) ?? m_checkedPars.FirstOrDefault())
                ?.Clone(Session.Part.DoPart);
            List<int> okParts = new List<int>();
            List<int> nokParts = new List<int>();
            List<long> partForProcess = new List<long>();
            int offset = 0;
            bool incToolUsage = true;
            if (m_testAoi.SubboardResults.ContainsKey(0) == false)
            {
                offset = 1;
            }
            // rozdeleni dilu na OK a NOK
            DivideParts(ref okParts, ref nokParts, offset);
            PrepareParts(ref partForProcess, okParts);
            Session.Rack.DoRack.Copy(m_rack);
            // Ulozeni OK dilu
            if (partForProcess.Count() > 0)
            {
                ProcessParts(1, partForProcess, incToolUsage, okParts, offset, "Ok");
                incToolUsage = false;
            }
            PrepareParts(ref partForProcess, nokParts);
            // Ulozeni NOK dilu
            if (partForProcess.Count() > 0)
            {
                ProcessParts(m_ngMSStatus, partForProcess, incToolUsage, nokParts, offset, "Ng");
            }
            PartsToRack(nokParts);

            Session.Rack.DoRack.Clear();
            m_rack.Clear();
            Session.Part.DoPart.Clear();
            // nahrad predchozi seznam OID dilu aktualnimi pouze pokud nelze hned retestovat stejné díly 
            if (!m_allowRetestImmediately)
            {
                m_prevCheckedPars.Clear();
                m_prevCheckedPars.AddRange(m_checkedPars.Select(x => x.PartID));
            }
            // a vymaz seznam aktualne nactenych dilu
            m_checkedPars.Clear();
        }

        private void ProcessParts(int partMSStatus, List<long> partsForProcess, bool incrementToolUsage, List<int> listOfParts, int offset, string result)
        {
            var ll = new List<Bartech.SGCore.Model.ContextData.PartData>();
            Session.Part.DoPart.MS = partMSStatus;
            // nastartuj dily  
            Session.Part.DoPart.MassStartEnd(partsForProcess, Session.UserWorkGroup.UserWorkGroupID, 0, false, incrementToolUsage: incrementToolUsage,
               okLog: (args) =>
               {
                   var uu = args[0] as PartOperationData;
                   foreach (var part in uu.Parts)
                   {
                       ll.Add(part);
                   }
                   Message = Log(LogMsg.PV_PartsSaved, Session.CycleContext.Tools[0].ToolCD, m_checkedPars.Count());
                   return null;
               });
            // zapis dilum procesni data
            for (int i = 0; i < ll.Count(); i++)
            {
                Session.Part.DoPart.SaveProcessDataAfterPartEnd(m_testAoi.CreateProcessData(listOfParts[i] + offset, result, m_checkedPars[listOfParts[i]].CurBarcode), string.Empty, ll[i].OID, (long)ll[i].DetailID, partMSStatus == 1 ? PartDataStatus.OK : PartDataStatus.Fail, DateTime.MinValue);
            }
        }

        private void DivideParts(ref List<int> okParts, ref List<int> nokParts, int offset)
        {
            foreach (var item in m_testAoi.SubboardResults)
            {
                if (item.Value.Last() == "FAIL")
                {
                    nokParts.Add(item.Key - offset);
                }
                else if (item.Value.Last() == "GOOD")
                {
                    okParts.Add(item.Key - offset);
                }
            }
        }

        private void PartsToRack(List<int> nokParts)
        {
            if (m_partInRackAction == WhatHappenToParts.allInFrame)
            {
                if (nokParts.Count() > 0) // pokud je alespoň jeden díl NOK, tak dáme do RACKu všechny, jinak tam nedáme nic
                {
                    for (int i = 0; i < m_checkedPars.Count(); i++)
                    {
                        Session.Rack.DoRack.AddPart(m_checkedPars[i].PartID);
                    }
                }
            }
            else
            {
                for (int i = 0; i < nokParts.Count(); i++)
                {
                    Session.Rack.DoRack.AddPart(m_checkedPars[nokParts[i]].PartID);
                }
            }
        }

        private void PrepareParts(ref List<long> partsForProcess, List<int> parts)
        {
            partsForProcess.Clear();
            foreach (var index in parts)
            {
                partsForProcess.Add(m_checkedPars[index].PartID);
            }
        }

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

    #region Enum
    public enum WhatHappenToParts
    {
        allInFrame,
        onlyNgInFrame,
    }
    #endregion
}