SMTFujiParser.cs
*SMTFujiParser.cs*
using System;
using System.Threading;
using Bartech.SGCore.Base;
using Bartech.SGCore.Local;
using Bartech.SGCore.Logic.NVCZ.Modules.SMT.Model.FUJI;
using DevExpress.Xpo;
using DevExpress.Xpo.DB;
using DevExpress.Xpo.Metadata;
using System.Collections.Generic;
using Bartech.SGCore.Local.Services;
using Bartech.SGCore.Logic.NVCZ.Modules.LaserParser.Model;
using System.Linq;
using xdata = xTrace03_SG_Data;
using System.Collections.Concurrent;
using Bartech.xTrace.NVCZ;
using Bartech.SGCore.Local.DataObjects;
using Bartech.SGCore.Base.Web;
using Bartech.SGCore.Model.ContextData;
using Bartech.SGCore.Helpers;
using Bartech.SGCore.Model.Messages;
using Bartech.SGCore.Model;
namespace Bartech.SGCore.Logic.NVCZ.Modules
{
[XModule("SMTFujiParser", "Modul pro parsovaní SMT FUJI", ModuleGroupType.Other, applicationType: xTraceApplicationType.NvCzSMT)]
public class SMTFujiParser : SGModuleLocal
{
class InternalData
{
private DateTime m_LastScanDT;
public InternalData()
{
MCID = string.Empty;
LINEID = string.Empty;
MaxModuleNo = 0;
Clear();
}
public string MCID { get; set; }
public int MaxModuleNo { get; set; }
public BoardTrace LastBoard { get; set; }
public DateTime LastScanDT { get { return m_LastScanDT; } set { m_LastScanDT = value; } }
public bool CompareWithBom { get; set; }
public bool CompareResult { get; set; }
public int ScanInterval { get; set; }
public bool m_SendPickOffErrorAlarm { get; set; }
public bool ParseOnlyNoReads { get; set; }
public string LINEID { get; set; }
public void Clear()
{
LastBoard = new BoardTrace();
CompareResult = false;
LastScanDT = DateTime.Now;
}
}
public SMTFujiParser(SGSession session, MSSQL cData)
: base(session)
{
m_cData = cData;
l_MachineStatus = new List<MachineStatus>();
m_privateData = new InternalData();
Session.Initialized += new EventHandler((s, e) =>
{
InitXPO(m_cData);
});
m_dxSession = Session.Resolve<SGDxSessionLocker>();
m_smtFujiSvc = Session.Resolve<SmtFujiService>();
Session.Messenger.Register<ObjectChangedMessage<BusUserGroup>>(this, (msg) => { OnUserChanged(msg); });
Session.ProcessContext.InitFujiTraceData();
//AllowedInDevs = new SGDeviceType[] { SGDeviceType.Scanner };
}
#region privatni promenne
protected volatile bool m_shouldStop = false;
protected object m_lock = new object();
private IDataLayer m_cDataDL;
private SGDxSessionLocker m_dxSession;
private MSSQL m_cData;
private Thread m_parseThread;
private bool m_enabled = false;
private InternalData m_privateData;
private List<MachineStatus> l_MachineStatus;
private bool m_threadStarted = false;
private bool m_useBlocker;
private int m_ioBlockDefinition = 1;
private ConcurrentQueue<BoardTrace> m_BoardQueue;
#endregion
protected SmtFujiService m_smtFujiSvc;
#region public promenne
//public FujiTrace FujiTraceData { get { return m_FujiTrace; } private set { m_FujiTrace = value; } }
#endregion
private void InitXPO(MSSQL cdata)
{
#region Inicializace XPO pro 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
}
protected override bool OnSetParameters()
{
SetOK();
m_privateData.MCID = Session.SysParams.TryGetString(this, "MCID", "", "MCID linky FUJI pro kterou budeme osazovací data nacítat");
m_privateData.CompareWithBom = Session.SysParams.GetParamValue<bool>(this, "CompareWithBom", true, "Jestli se mají nactená osazovací data porovnat s kusovníkem");
m_privateData.ScanInterval = Session.SysParams.GetParamValue<int>(this, "ScanInterval", 3, "Interval v sekundách skenování DB Fuji") * 1000;
m_privateData.m_SendPickOffErrorAlarm = Session.SysParams.GetBoolean(this, "SendPickOffErrorAlarm", false, "Jestli se má odeslat alarm 5xtejných PICKOFFERRORS behem hodiny");
m_privateData.ParseOnlyNoReads = Session.SysParams.GetBoolean(this, "ParseOnlyNoReads", false, "Jestli ma vlakno parsovat jen NOREADy, barcode jinak dojde pres HandlePortData");
m_privateData.LINEID = Session.SysParams.TryGetString(this, "LINEID", "", "ID linky FUJI pro kterou budeme osazovací data nacítat (nahrazuje MCID)");
return m_enabled = CallResult;
}
void OnUserChanged(ObjectChangedMessage<BusUserGroup> msg)
{
if (msg.Data == null || msg.Data?.OID == 0)
m_smtFujiSvc.DeactivateActualJob(m_cDataDL, SetError, m_privateData.LINEID);
}
public override void StartLevel(SGTreeModuleData mdata)
{
// podivam se jestli je nacteny job, operace a kusovnik a mozu nacitat jen noready
if (Session.Job.DoJob.IsSelected && Session.CycleContext.OperationOverride != null && !string.IsNullOrEmpty(m_privateData.LINEID))
{
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Nastavuji linku {m_privateData.LINEID}: VO={Session.Job.DoJob.CustomerBarcode}, Operace={Session.CycleContext.OperationOverride.RoutingPosIndex}");
m_smtFujiSvc.SetActualJob(m_cDataDL, SetError, m_privateData.LINEID, Session.WorkPlace.WorkPlaceID, Session.Job.DoJob.JobID, Session.CycleContext.OperationOverride.RoutingItemID, Session.CycleContext.OperationOverride.RoutingPosIndex, Session.CycleContext.OperationOverride.OID, Session.UserWorkGroup.MainUser.UserID, Session.UserWorkGroup.UserWorkGroupID, GetProgramName(string.Empty));
// nastav joba
if (!m_threadStarted)
{
CreateKPIs();
Session.ProcessContext.CurrentFujiData.Clear();
// spusteni parsovaciho vlakna
m_threadStarted = StartThread();
}
}
if (Session.Part.DoPart.IsPartStarted && !string.IsNullOrEmpty(Session.Part.CurBarcode))
{
LoadFujiDataForBarcode_v2(Session.Part.CurBarcode, Session.Part.DoPart.DetailID);
}
}
public override SGLMState ModuleChecker(SGTreeModuleData mdata)
{
if (Session.Part.DoPart.IsPartStarted && Session.Part.DoPart.GS == 35)
{
Session.Part.DoPart.MS = 35;
}
return SGLMState.Handled | SGLMState.OK | SGLMState.UpLevel;
}
/// <summary>
/// Pokusi se nacit osazovaci data pro barcode, ktery prisel normalne pres HandlePortData do modulu PartStart, ktery musi byt pred timto modulem
/// </summary>
/// <param name="barcode"></param>
public void LoadFujiDataForBarcode(string barcode, long? detailID)
{
// pokusim se najit data
PcbTrace pcb = m_smtFujiSvc.GetDonePcb(m_cDataDL, m_privateData.LINEID, barcode);
if (pcb != null && !string.IsNullOrEmpty(pcb.PCBID))
{
Log(LogMsg.CUSTOM_DEBUG_MSG, $"FUJI osadila panel PCBID={pcb.PCBID}, PCBRECNO={pcb.PCBRECNO}, SIDE={pcb.SIDE}, STARTTIME='{pcb.STARTTIME}', ENDTIME='{pcb.ENDTIME}'.");
LoadPcbData(pcb.PCBID, pcb.PCBRECNO, detailID);
}
else
Message = Log(LogMsg.SMT_NO_PLACE_DATA, barcode);
}
public void LoadFujiDataForBarcode_v2(string barcode, long detailID)
{
PcbTrace pcb = m_smtFujiSvc.GetDonePcb(m_cDataDL, m_privateData.LINEID, barcode);
if (pcb != null && !string.IsNullOrEmpty(pcb.PCBID))
{
Log(LogMsg.CUSTOM_DEBUG_MSG, $"FUJI osadila panel PCBID={pcb.PCBID}, PCBRECNO={pcb.PCBRECNO}, SIDE={pcb.SIDE}, STARTTIME='{pcb.STARTTIME}', ENDTIME='{pcb.ENDTIME}'.");
LoadPcbData_v2(pcb.PCBID, pcb.PCBRECNO, detailID);
}
else
Message = Log(LogMsg.SMT_NO_PLACE_DATA, barcode);
}
private bool LoadPcbData(string pcbid, string pcbrecno, long? detailID)
{
bool r = false;
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Nacítám data z FUJI pro panel:pcbid='{pcbid}', pcbrecno={pcbrecno}");
Session.ProcessContext.CurrentFujiData.Board = m_smtFujiSvc.LoadBoardTraceData(m_cDataDL, m_privateData.MCID, Session.Job.DoJob.JobID, pcbid, pcbrecno);
// nactu si pcbtrace z xtrace db
if (!Session.ProcessContext.CurrentFujiData.HasBoard)
{
// nenacetl osazovaci datapanelu z fuji db
Log(LogMsg.CUSTOM_WARNING_MSG, $"Data pro panel '{pcbid}' nebyla ve FUJIDB nalezena!");
return false;
}
if (!Session.ProcessContext.CurrentFujiData.Board.ISNEW)
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Opakované nactení panelu ID={Session.ProcessContext.CurrentFujiData.Board.OID},SN={Session.ProcessContext.CurrentFujiData.Board.BOARDID}");
if (m_privateData.LastBoard.BOARDID != Session.ProcessContext.CurrentFujiData.Board.BOARDID)
{
BoardTrace board = Session.ProcessContext.CurrentFujiData.Board;
// nacetl docti data stroje chyby
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Sériové císlo '{pcbid}': Strana={board.SIDE}, Pocet desek={board.NUMBLOCKS}, Pocet komponent={board.NUMCOMP}, Pocet chyb={board.NUMERRORS}.");
// nactu osazovaci data
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Nacítám osazovací data...Operace={Session.CycleContext.OperationOverride.RoutingPosIndex}, RoutingItemID={Session.CycleContext.OperationOverride.RoutingItemID}");
int inspectedColumns = 0;
int inspectedRows = 0;
board.Placements = m_smtFujiSvc.LoadPlacementData(m_cDataDL, SetError, m_privateData.MCID, Session.Job.DoJob.JobID, pcbid, Session.WorkPlace.WorkPlaceID, Session.CycleContext.OperationOverride.RoutingPosIndex, Session.CycleContext.OperationOverride.OID, Session.CycleContext.OperationOverride.Name, Session.UserWorkGroup.MainUser.UserID, detailID, Session.CycleContext.OperationOverride.RoutingItemID, Session.UserWorkGroup.UserWorkGroupID, out inspectedColumns, out inspectedRows, true);
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Placements count={board.Placements.Count}, inspectedRows={inspectedRows}, inspectedColumns={inspectedColumns}");
// nacist device data (pocty, chyby)
Message = Log(LogMsg.CUSTOM_INFO_MSG, "Nacítám device trace data...");
List<DeviceTrace> deviceTrace = m_smtFujiSvc.LoadDeviceTrace(m_cDataDL, m_privateData.MCID, board.JOBID, board.BOARDID);
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Device trace count={deviceTrace.Count}");
Session.ProcessContext.CurrentFujiData.DeviceTrace.NewTraceData(deviceTrace);
Session.ProcessContext.CurrentFujiData.SummarizedTraceData.SummarizeDeviceTrace(Session.ProcessContext.CurrentFujiData.DeviceTrace.SourceData);
/*
foreach (var d in m_FujiTrace.SummarizedTraceData.DeviceTraceSummary)
Log(LogMsg.CUSTOM_INFO_MSG, $"SUM****DEVICEKEY={d.DEVICEKEY}, FEEDCOUNT={d.KPIFEEDCOUNT.GetLastValue()}+{d.KPIFEEDCOUNT.LastIncrement}=>{d.KPIFEEDCOUNT.GetValue()}, REJECTPARTS={d.KPIREJECTPARTS.GetLastValue()}+{d.KPIREJECTPARTS.LastIncrement}=>{d.KPIREJECTPARTS.GetValue()}, NOPICKUPCOUNT={d.KPINOPICKUPCOUNT.GetLastValue()}+{d.KPINOPICKUPCOUNT.LastIncrement}=>{d.KPINOPICKUPCOUNT.GetValue()}, PICKUPERRORS={d.KPIPICKUPERRORS.GetLastValue()}+{d.KPIPICKUPERRORS.LastIncrement}=>{d.KPIPICKUPERRORS.GetValue()}, VISIONERRORS={d.KPIVISIONERRORS.GetLastValue()}+{d.KPIVISIONERRORS.LastIncrement}=>{d.KPIVISIONERRORS.GetValue()} ****");
*/
//foreach (var d in m_FujiTrace.SummarizedTraceData.DeviceTraceTotalSummary)
var t = Session.ProcessContext.CurrentFujiData.SummarizedTraceData.DeviceTraceTotalSummary;
//Log(LogMsg.CUSTOM_INFO_MSG, $"SUMTOTAL****DEVICEKEY={t.DEVICEKEY}, FEEDCOUNT={t.KPIFEEDCOUNT.GetLastValue()}+{t.KPIFEEDCOUNT.LastIncrement}=>{t.KPIFEEDCOUNT.GetValue()}, REJECTPARTS={t.KPIREJECTPARTS.GetLastValue()}+{t.KPIREJECTPARTS.LastIncrement}=>{t.KPIREJECTPARTS.GetValue()}, NOPICKUPCOUNT={t.KPINOPICKUPCOUNT.GetLastValue()}+{t.KPINOPICKUPCOUNT.LastIncrement}=>{t.KPINOPICKUPCOUNT.GetValue()}, PICKUPERRORS={t.KPIPICKUPERRORS.GetLastValue()}+{t.KPIPICKUPERRORS.LastIncrement}=>{t.KPIPICKUPERRORS.GetValue()}, VISIONERRORS={t.KPIVISIONERRORS.GetLastValue()}+{t.KPIVISIONERRORS.LastIncrement}=>{t.KPIVISIONERRORS.GetValue()} ****");
SaveDeviceTraceKPI(t);
var tt = Session.ProcessContext.CurrentFujiData.SummarizedTraceData.DeviceTraceSummaryByFeeder;
/*
foreach (var d in tt)
Log(LogMsg.CUSTOM_INFO_MSG, $"SUM****FEEDER={d.FIDL}, FEEDCOUNT={d.KPIFEEDCOUNT.GetLastValue()}+{d.KPIFEEDCOUNT.LastIncrement}=>{d.KPIFEEDCOUNT.GetValue()}, REJECTPARTS={d.KPIREJECTPARTS.GetLastValue()}+{d.KPIREJECTPARTS.LastIncrement}=>{d.KPIREJECTPARTS.GetValue()}, NOPICKUPCOUNT={d.KPINOPICKUPCOUNT.GetLastValue()}+{d.KPINOPICKUPCOUNT.LastIncrement}=>{d.KPINOPICKUPCOUNT.GetValue()}, PICKUPERRORS={d.KPIPICKUPERRORS.GetLastValue()}+{d.KPIPICKUPERRORS.LastIncrement}=>{d.KPIPICKUPERRORS.GetValue()}, VISIONERRORS={d.KPIVISIONERRORS.GetLastValue()}+{d.KPIVISIONERRORS.LastIncrement}=>{d.KPIVISIONERRORS.GetValue()} ****");
*/
SaveFeederTraceKPI(tt);
// nacist nozzletrac data
Message = Log(LogMsg.CUSTOM_INFO_MSG, "Nacítám nozzle trace data...");
List<NozzleTrace> nozzleTrace = m_smtFujiSvc.LoadNozzleTrace(m_cDataDL, m_privateData.MCID, board.JOBID, board.BOARDID);
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Nozzle trace count={nozzleTrace.Count}");
// porovnat nactene komponenty s kusovnikem, jestli to sedi (porovnat jen SX soucastky nebo aj reference?)
if (m_privateData.CompareWithBom)
{
// funcke by mela vratit seznam co nesedi. pokud je prazdny pak je to OK, jinak by mela vypsat rozdili a pripadne zavolat alarm
ComparePlacementDataWithBom(board);
}
// zapis ProductionData pro KPI..
// zapamatovat posledni panel
m_privateData.LastBoard = board;
// pokud se jedna o davkou - NOREAD potom zapisu osazovaci data - soucastky k JobOperation
if (board.BOARDID.ToUpper().Contains("NOREAD"))
{
Message = Log(LogMsg.SMD_CHECKER_Noread);
Session.CycleContext.KPICounters.FirstOrDefault(k => k.Name == "NOREADS")?.Inc();
SaveJobOperationInputBach(board);
}
r = true;
}
return r;
}
private bool LoadPcbData_v2(string pcbid, string pcbrecno, long? detailID)
{
bool r = false;
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Nacítám data z FUJI pro panel:pcbid='{pcbid}', pcbrecno={pcbrecno}");
Session.ProcessContext.CurrentFujiData.Board = m_smtFujiSvc.LoadBoardTraceData(m_cDataDL, m_privateData.MCID, Session.Job.DoJob.JobID, pcbid, pcbrecno);
//m_smtFujiSvc.InsertBarcodeToQueue(m_cDataDL, m_privateData.MCID, Session.Job.DoJob.JobID, Session.CycleContext.OperationOverride.RoutingPosIndex, detailID, pcbid, Session.CycleContext.OperationOverride.Side);
// nactu si pcbtrace z xtrace db
if (!Session.ProcessContext.CurrentFujiData.HasBoard)
{
// nenacetl osazovaci datapanelu z fuji db
Log(LogMsg.CUSTOM_WARNING_MSG, $"Data pro panel '{pcbid}' nebyla ve FUJIDB nalezena!");
return false;
}
if (!Session.ProcessContext.CurrentFujiData.Board.ISNEW)
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Opakované nactení panelu ID={Session.ProcessContext.CurrentFujiData.Board.OID},SN={Session.ProcessContext.CurrentFujiData.Board.BOARDID}");
if (m_privateData.LastBoard.BOARDID != Session.ProcessContext.CurrentFujiData.Board.BOARDID)
{
BoardTrace board = Session.ProcessContext.CurrentFujiData.Board;
// nacetl docti data stroje chyby
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Sériové císlo '{pcbid}': Strana={board.SIDE}, Pocet desek={board.NUMBLOCKS}, Pocet komponent={board.NUMCOMP}, Pocet chyb={board.NUMERRORS}.");
// nactu osazovaci data
/*
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Nacítám osazovací data...Operace={Session.CycleContext.OperationOverride.RoutingPosIndex}, RoutingItemID={Session.CycleContext.OperationOverride.RoutingItemID}");
int inspectedColumns = 0;
int inspectedRows = 0;
board.Placements = m_smtFujiSvc.LoadPlacementData(m_cDataDL, SetError, m_privateData.MCID, Session.Job.DoJob.JobID, pcbid, Session.WorkPlace.WorkPlaceID, Session.CycleContext.OperationOverride.RoutingPosIndex, Session.CycleContext.OperationOverride.OID, Session.CycleContext.OperationOverride.Name, Session.UserWorkGroup.MainUser.UserID, detailID, Session.CycleContext.OperationOverride.RoutingItemID, Session.UserWorkGroup.UserWorkGroupID, out inspectedColumns, out inspectedRows, true);
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Placements count={board.Placements.Count}, inspectedRows={inspectedRows}, inspectedColumns={inspectedColumns}");
*/
// nacist device data (pocty, chyby)
Message = Log(LogMsg.CUSTOM_INFO_MSG, "Nacítám device trace data...");
List<DeviceTrace> deviceTrace = m_smtFujiSvc.LoadDeviceTrace(m_cDataDL, m_privateData.MCID, board.JOBID, board.BOARDID);
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Device trace count={deviceTrace.Count}");
Session.ProcessContext.CurrentFujiData.DeviceTrace.NewTraceData(deviceTrace);
Session.ProcessContext.CurrentFujiData.SummarizedTraceData.SummarizeDeviceTrace(Session.ProcessContext.CurrentFujiData.DeviceTrace.SourceData);
var t = Session.ProcessContext.CurrentFujiData.SummarizedTraceData.DeviceTraceTotalSummary;
SaveDeviceTraceKPI(t);
var tt = Session.ProcessContext.CurrentFujiData.SummarizedTraceData.DeviceTraceSummaryByFeeder;
SaveFeederTraceKPI(tt);
// nacist nozzletrac data
Message = Log(LogMsg.CUSTOM_INFO_MSG, "Nacítám nozzle trace data...");
List<NozzleTrace> nozzleTrace = m_smtFujiSvc.LoadNozzleTrace(m_cDataDL, m_privateData.MCID, board.JOBID, board.BOARDID);
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Nozzle trace count={nozzleTrace.Count}");
// porovnat nactene komponenty s kusovnikem, jestli to sedi (porovnat jen SX soucastky nebo aj reference?)
/*
if (m_privateData.CompareWithBom)
{
// funcke by mela vratit seznam co nesedi. pokud je prazdny pak je to OK, jinak by mela vypsat rozdili a pripadne zavolat alarm
ComparePlacementDataWithBom(board);
}
*/
// zapis ProductionData pro KPI..
// zapamatovat posledni panel
m_privateData.LastBoard = board;
// pokud se jedna o davkou - NOREAD potom zapisu osazovaci data - soucastky k JobOperation
/*
if (board.BOARDID.ToUpper().Contains("NOREAD"))
{
Message = Log(LogMsg.SMD_CHECKER_Noread);
Session.CycleContext.KPICounters.FirstOrDefault(k => k.Name == "NOREADS")?.Inc();
SaveJobOperationInputBach(board);
}
*/
r = true;
}
return r;
}
private void SaveJobOperationInputBach(BoardTrace board)
{
Session.JobOperation.Clear();
Session.JobOperation.OperationCD = Session.CycleContext.OperationOverride.OperationCD;
Session.JobOperation.DoJobOperation.UseRouting = false;
Session.JobOperation.Create(true);
var prodSvc = Session.Resolve<ProductService>();
List<Placement> ibNoMatch = new List<Placement>();
//foreach (var p in board.Placements.GroupBy(g => g.DID).Select(x => x.First()).ToList())
foreach (var p in board.Placements)
{
DOInputBatch ib = new DOInputBatch(this);
string batchCD = string.Empty;
var product = prodSvc.GetProduct(m_dxSession, p.ComponentCD);
if (product != null)
{
bool inputBatchExists = ib.Load(p.BatchCD, product.OID);
if (inputBatchExists)
{
// zavedu sarzi
Session.JobOperation.AddInputBatch(ib, true);
}
else
{
ibNoMatch.Add(p);
}
}
else
{
Message = Log(LogMsg.CUSTOM_WARNING_MSG, $"Soucástka {p.ComponentCD} nenelazene v císelníku MES!");
}
}
// priradim operaci carovy kod a ukoncim ji
Session.JobOperation.DoJobOperation.AssignBarcode(board.BOARDID);
Session.JobOperation.DoJobOperation.QtyPass = 0;//(int)board.NUMBLOCKS; // pocet desek na panelu
Session.JobOperation.DoJobOperation.QtyFail = 0;
Session.JobOperation.DoJobOperation.QtyScrap = 0;
Session.JobOperation.Update();
Session.JobOperation.DoJobOperation.EndOperationType = DOJobOperation.JobEndOperationType.CallRelease;
Session.JobOperation.End(false, 1, false, false, false);
if (ibNoMatch.Count > 0)
{
Message = Log(LogMsg.CUSTOM_WARNING_MSG, $"Nekteré šarže neexistují v MES a nebyly uložené: {string.Join(",", ibNoMatch.Select(x => x.DID).ToArray())}");
}
}
private void LoadMachineStatus()
{
l_MachineStatus.Clear();
using (var uw = new UnitOfWork(m_cDataDL))
{
SelectedData selectedData = uw.ExecuteQuery($"SELECT MCID, MODULENO, LINENAME FROM dbo.FUJIDB_MACHINESTATUS2");
foreach (SelectStatementResultRow row in selectedData.ResultSet[0].Rows)
{
var machineStatus = new MachineStatus();
machineStatus.MCID = row.Values[0].ToString();
machineStatus.MODULENO = (decimal)row.Values[1];
machineStatus.LINENAME = row.Values[2].ToString();
l_MachineStatus.Add(machineStatus);
}
}
}
private bool StartThread()
{
if (!m_enabled || m_privateData.ScanInterval == 0) return false;
m_parseThread = new Thread(new ThreadStart(ParseThread2));
m_parseThread.Name = "SMTFUJIParseThread";
m_parseThread.IsBackground = true;
m_parseThread.Start();
return true;
}
private void ParseThread()//zde beží samostatné background vlákno
{
int warningRepeat = 3;
Message = Log(LogMsg.CUSTOM_DEBUG_MSG, $"Spuštím parsovací vlákno z FUJI DB pro MCID={m_privateData.MCID}");
m_shouldStop = false;
while (!m_shouldStop)
{
try
{
//TO-DO
if (!Session.Job.DoJob.IsSelected)
{
StopThread();
}
// nactu status stroje - jednotlive moduly
LoadMachineStatus();
Session.CycleContext.KPICounters.FirstOrDefault(k => k.Name == "TICKS")?.Inc();
int maxModuleNo = 0;
if (l_MachineStatus.Any(o => o.MCID == m_privateData.MCID))
{
maxModuleNo = (int)l_MachineStatus.Where(o => o.MCID == m_privateData.MCID).Max(o => o.MODULENO);
}
if (maxModuleNo == 0 && warningRepeat-- > 0)
{
Log(LogMsg.CUSTOM_WARNING_MSG, $"Nepodarilo se nacíst Max(MODULENO) na MCID {m_privateData.MCID}. Osazené panely se nebudou automaticky zpracovávat.");
Thread.Sleep(m_privateData.ScanInterval);
continue;
}
if (m_privateData.MaxModuleNo != maxModuleNo)
{
Log(LogMsg.CUSTOM_DEBUG_MSG, $"Nastavuji parametr MaxModuleNo={maxModuleNo} pro {l_MachineStatus.Where(o => o.MCID == m_privateData.MCID).FirstOrDefault().LINENAME}");
m_privateData.MaxModuleNo = maxModuleNo;
}
DateTime lastScanDT = m_privateData.LastScanDT;
List<PcbTrace> newPcbs = m_smtFujiSvc.GetDonePcbs(m_cDataDL, m_privateData.MCID, m_privateData.LastScanDT, maxModuleNo.ToString());
if (newPcbs.Count > 0)
{
Log(LogMsg.CUSTOM_DEBUG_MSG, $"{m_privateData.LastScanDT}: Celkem nalezeno panelu {newPcbs.Count}");
m_privateData.LastScanDT = newPcbs.Max(o => o.DTE);
// projdi SN desek a posli je do HandlePortData
foreach (var pcb in newPcbs)
{
// pokud mam nacitat jen noread a neni to noread tak nic neparsuju
if (m_privateData.ParseOnlyNoReads && !pcb.PCBID.ToUpper().StartsWith("NOREAD"))
continue;
Log(LogMsg.CUSTOM_DEBUG_MSG, $"FUJI osadila panel PCBID={pcb.PCBID}, PCBRECNO={pcb.PCBRECNO}, SIDE={pcb.SIDE}, STARTTIME='{pcb.STARTTIME}', ENDTIME='{pcb.ENDTIME}'.");
bool isPcbData = LoadPcbData(pcb.PCBID, pcb.PCBRECNO, null);
// nepreposilam pokud mam parsovat jen noready
if (!m_privateData.ParseOnlyNoReads)
Session.SetNextData(Session.Data.CreateReceivedData(SGDeviceType.Scanner, pcb.PCBID));
}
}
else
m_privateData.LastScanDT = DateTime.Now;
}
catch (Exception ex)
{
Log(LogMsg.CUSTOM_WARNING_MSG, ex.Message);
}
Thread.Sleep(m_privateData.ScanInterval);
}
}
private void ParseThread2()//zde beží samostatné background vlákno
{
Message = Log(LogMsg.CUSTOM_DEBUG_MSG, $"Spuštím parsovací vlákno z FUJI DB pro LINENAME={m_privateData.LINEID}");
m_shouldStop = false;
while (!m_shouldStop)
{
try
{
//TO-DO
if (!Session.Job.DoJob.IsSelected)
{
// odnastav joba
if (Session.SysCommands.LastSysCommandType == SGSysCommandType.JobEnd)
{
Session.SysCommands.ClearLastSysCommandType();
m_smtFujiSvc.DeactivateActualJob(m_cDataDL, SetError, m_privateData.LINEID, true);
}
// stopni vlakno
StopThread();
break;
}
Session.CycleContext.KPICounters.FirstOrDefault(k => k.Name == "TICKS")?.Inc();
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Overuji nastavení linky {m_privateData.LINEID}: VO={Session.Job.DoJob.CustomerBarcode}, Operace={Session.CycleContext.OperationOverride.RoutingPosIndex}");
// tady prectu informaci z nastaveni jobu
JobTrace jobTrace = m_smtFujiSvc.GetActualJob(m_cDataDL, SetError, m_privateData.LINEID, Session.Job.DoJob.JobID, Session.CycleContext.OperationOverride.RoutingPosIndex);
if (jobTrace != null)
{
Session.ProcessContext.SetSmtData(new SMTData()
{
LineName = jobTrace.LineName,
CustomJobBD = jobTrace.CustomJobBD,
DTS = jobTrace.DTS,
RoutingPosIndex = jobTrace.RoutingPosIndex,
ProgramName = jobTrace.ProgramName,
BarcodeCount = jobTrace.BarcodeCount,
NoReadCount = jobTrace.NoReadCount,
LastBarcode = jobTrace.LastBarcode,
LastBarcodeDT = jobTrace.LastBarcodeDT,
IsActive = jobTrace.IsActive
});
FullyObservableCollection<SMTPanel> panels = m_smtFujiSvc.GetCurrentPanels(m_cDataDL, SetError, m_privateData.LINEID, Session.Job.DoJob.JobID, Session.CycleContext.OperationOverride.RoutingPosIndex);
Session.ProcessContext.SmtData.SetCurrentPanels(panels);
//Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Stav VO {jobTrace.CustomJobBD} na FUJI: Datum od={jobTrace.DTS.ToString()}, Operace={jobTrace.RoutingPosIndex}, Aktivní={jobTrace.IsActive}, Celkem barkódu={jobTrace.BarcodeCount}, Celkem NOREAD={jobTrace.NoReadCount}, Poslední barkód={jobTrace.LastBarcode} ({jobTrace.LastBarcodeDT})");
}
else
{
Message = Log(LogMsg.CUSTOM_WARNING_MSG, $"FUJI parser nemá nastaven v DB aktivní VO a operaci!");
}
}
catch (Exception ex)
{
Log(LogMsg.CUSTOM_WARNING_MSG, ex.Message);
}
Thread.Sleep(m_privateData.ScanInterval);
}
}
private void StopThread()
{
if (m_parseThread != null && m_parseThread.IsAlive)
{
Thread.Sleep(1);
m_shouldStop = true;
lock (m_lock) { Monitor.Pulse(m_lock); }
m_parseThread.Join(3000);
m_parseThread = null;
m_threadStarted = false;
}
}
/// <summary>
/// Funkce vezme senam soucastek a referenci podle FUIJI placement dat a porovna je s tim co je v kusovniku BOM
/// </summary>
private List<Component> ComparePlacementDataWithBom(BoardTrace board)
{
// nactu si kusovník
//List<Component> compareList = new List<Component>();
List<Component> comparedList = new List<Component>();
using (m_dxSession.GetLock())
{
// pozor na FUJI se nactitaji komponenty primo ze stroje a v kusovniku z QAD mani priznak nenactitat IsRequested=false.
var bomComponents = (from j in new XPQuery<xdata.Job>(m_dxSession.Session)
join bi in new XPQuery<xdata.JobBomItem>(m_dxSession.Session) on j.JobBom.OID equals bi.JobBom.OID
where j.OID == Session.Job.DoJob.JobID && (bi.RoutingPosIndex == Session.CycleContext.OperationOverride.RoutingPosIndex) && (bi.Quantity > 0) && (bi.Deleted == false)
select bi).ToList();
// list obsahuje objekt.DID (soucastka_mesezera_sarze) a objekt.REFERENCE
var fujiComponents = board.Placements.Select(m => new { m.ComponentCD, m.REFERENCE }).Distinct().ToList();
// projdi koleckci porovnavej jestli je soucastka na dane referenci v kusovniku
DOKatalogParams katalog = new DOKatalogParams(this, "Material");
foreach (var fc in fujiComponents)
{
var bc = bomComponents.FirstOrDefault(o => o.Material.ProductCD == fc.ComponentCD && o.Position.PositionCD == fc.REFERENCE);
// zjistit jestli se jedna o pastu
if (bc != null)
{
if (bc.Material.OID == 0) continue;
katalog.Load(bc.Material.OID);
if (!katalog.GetValue<bool>("IsPaste", false, true))
{
Component c = new Component() { Code = fc.ComponentCD, Reference = fc.REFERENCE, Matched = (bc != null) };
comparedList.Add(c);
}
}
else
{
Component c = new Component() { Code = fc.ComponentCD, Reference = fc.REFERENCE, Matched = false };
comparedList.Add(c);
}
}
int matched = comparedList.Where(o => o.Matched).Count();
int notMatched = comparedList.Where(o => !o.Matched).Count();
if (notMatched > 0)
{
Message = Log(LogMsg.SMT_ComparePlacementError, bomComponents.Count, fujiComponents.Count, notMatched);
foreach (Component item in comparedList.Where(c => c.Matched == false))
{
Message = Log(LogMsg.CUSTOM_INFO_MSG, $"Soucástka: {item.Code}, reference: {item.Reference}");
}
}
}
return comparedList;
}
public void CreateKPIs()
{
SetOK();
try
{
Session.CycleContext.ClearKPICounters();
Session.CycleContext.KPICounters.Add(new Model.KPIData.KPICounter("TICKS"));
Session.CycleContext.KPICounters.Add(new Model.KPIData.KPICounter("FEEDCOUNT"));
Session.CycleContext.KPICounters.Add(new Model.KPIData.KPICounter("REJECTPARTS"));
Session.CycleContext.KPICounters.Add(new Model.KPIData.KPICounter("NOPICKUPCOUNT"));
Session.CycleContext.KPICounters.Add(new Model.KPIData.KPICounter("PICKUPERRORS"));
Session.CycleContext.KPICounters.Add(new Model.KPIData.KPICounter("VISIONERRORS"));
Session.CycleContext.KPICounters.Add(new Model.KPIData.KPICounter("NOREADS"));
}
catch (Exception ex)
{
SetError(ex);
}
LogResult();
}
private void SaveDeviceTraceKPI(DeviceTrace deviceTrace)
{
SetOK();
try
{
Session.CycleContext.KPICounters.FirstOrDefault(k => k.Name == "FEEDCOUNT")?.SetValue(deviceTrace.KPIFEEDCOUNT.GetValue());
Session.CycleContext.KPICounters.FirstOrDefault(k => k.Name == "REJECTPARTS")?.SetValue(deviceTrace.KPIREJECTPARTS.GetValue());
Session.CycleContext.KPICounters.FirstOrDefault(k => k.Name == "NOPICKUPCOUNT")?.SetValue(deviceTrace.KPINOPICKUPCOUNT.GetValue());
Session.CycleContext.KPICounters.FirstOrDefault(k => k.Name == "PICKUPERRORS")?.SetValue(deviceTrace.KPIPICKUPERRORS.GetValue());
Session.CycleContext.KPICounters.FirstOrDefault(k => k.Name == "VISIONERRORS")?.SetValue(deviceTrace.KPIVISIONERRORS.GetValue());
}
catch (Exception ex)
{
SetError(ex);
}
LogResult();
}
private void SaveFeederTraceKPI(List<DeviceTrace> feederTraceList)
{
SetOK();
try
{
foreach (var f in feederTraceList)
{
var kpi = Session.CycleContext.KPICounters.FirstOrDefault(k => k.Name == f.FIDL);
if (kpi == null)
{
Model.KPIData.KPIHourCounter hourKPI = new Model.KPIData.KPIHourCounter(f.FIDL);
hourKPI.OnCounterOverflow += HourKPI_OnCounterOverflow;
hourKPI.StartMonitor(5);
Session.CycleContext.KPICounters.Add(hourKPI);
}
else
Session.CycleContext.KPICounters.FirstOrDefault(k => k.Name == f.FIDL)?.SetValue(f.KPIPICKUPERRORS.GetValue());
}
}
catch (Exception ex)
{
SetError(ex);
}
LogResult();
}
private void HourKPI_OnCounterOverflow(object sender, Model.KPIData.HourCounterEventArgs e)
{
var k = sender as Model.KPIData.KPIHourCounter;
TimeSpan ts = e.Timestamp - e.ChangeCounter.State.FirstChangeTime;
string elapsetTime = string.Format("{0:2d}:{1:2d}", ts.Hours, ts.Minutes);
Log(LogMsg.CUSTOM_WARNING_MSG, $"Prekrocen pocet ({e.HourCounter.MaxChangesCount}) chybných osazení po sobe behem {e.HourCounter.HourInterval} hodin. Feeder:{e.HourCounter.Name}, Uplynulý cas:{elapsetTime}, {e.HourCounter.FirstValue}/{e.HourCounter.GetValue()}/{e.HourCounter.GetValue() - e.HourCounter.FirstValue}");
if (m_privateData.m_SendPickOffErrorAlarm)
Session.Alarm.SendAlarm(Session.UserWorkGroup.MainUser.UserID, SGAlarmEventType.None, "A20040", Session.Job.DoJob.CustomerBarcode, Session.Job.DoProduct.Barcode, string.Join(",", Session.UserWorkGroup.LoggedUsers.Select(u => u.Name)), e.HourCounter.Name, string.Format("Interval:1h, Parametr:PICKUPERRORS, Pocet:{0}", e.HourCounter.GetValue()));
}
/// <summary>
/// Vrat nastaveni hlavniho programu
/// </summary>
/// <param name="programName"></param>
/// <returns></returns>
private string GetProgramName(string programName = null)
{
return (string.IsNullOrEmpty(programName)) ? Session.Job.DoProduct.GmsKatalogParams["Hlavní program"] : programName;
}
}
}