using System.Collections.Generic; using OTSDataType; using System; using System.Drawing; using static OTSDataType.otsdataconst; using OTSModelSharp.ImageProcess; using OTSModelSharp.ServiceInterface; namespace OTSModelSharp { // enum and struct used for send message to App public enum ENUM_MSG_TYPE { MTHREADSTATUS = 1001, MSAMPLESTATUS = 1002, MSAMPLERESULT = 1003 }; public enum MSAMPLE_RET { BSE_DATA = 0, FIELD_DATA = 1, START_MSR_FIELD = 2 }; public struct SMSR_COMPLETE_DATA { public OTS_MSR_THREAD_STATUS MsrStatus; public string csMsrStartTime; public int iMsrCompleteSampleCount; public int iMsrCompleteFieldCount; public int iParticleCount; public TimeSpan MsrUsedTime; public string csMsrEndTime; }; public struct STMThreadStatus { public OTS_MSR_THREAD_STATUS iMsrStatu; //OTS_MSR_THREAD_STATUS public string csMsrStartTime; //MSR_START_TIME public string csMsrEndTime; //MSR_END_TIME public SMSR_COMPLETE_DATA SMsrCompleteData; }; public struct STMSampleStatus { public OTS_MSR_SAMPLE_STATUS iMsrSampleStatu; //OTS_MSR_SAMPLE_STATUS public string cSampleName; public string csSampleMsrStartTime; public List BCompleteFieldList; }; public struct STMSampleResultData { public MSAMPLE_RET iRetDataType; //ENUM_MEASURE_SAMPLE_RESULT public struct RBSEDATA { public System.Drawing.Point pos; public int iBSEDataHeight; public int iBSEDataWidth; public byte[] lpBSEData; }; public struct SAMPLEFIELDDATA { public System.Drawing.Point FieldPos; public int iMeasureFieldCount; public int iCompleteFieldCount; public int iSParticleCount; // Field particle count public TimeSpan TUsedTime; }; public struct StartToMsrField { public System.Drawing.Point FieldPos; }; public RBSEDATA BSEData; public SAMPLEFIELDDATA SFieldData; public StartToMsrField SMsrField; }; public struct ST_MSTMsg { public ENUM_MSG_TYPE iMsgType; public STMThreadStatus STMThreadStu; public STMSampleStatus STMSampleStu; public STMSampleResultData STMSampleRetData; }; public class CMsrThread { private const string UNTITLED_FILE_NAME = "Untitled"; public delegate void ProgressEventHandler(ST_MSTMsg msg); public event ProgressEventHandler ProgressEvent; COTSMsrPrjResultData m_pProjData; List< COTSSample> m_listMeasurableSamples; string m_strWorkingFolder; CMsrThreadStatus m_ThreadStatus; SemController m_SemController;// there is no correspondense clr,so use this instead temporarilly protected static NLog.Logger loger = NLog.LogManager.GetCurrentClassLogger(); public CMsrThread() { m_strWorkingFolder = ""; m_ThreadStatus = new CMsrThreadStatus(); m_SemController = new SemController(); } public COTSMsrPrjResultData GetProjResultData() { return m_pProjData; } public ISemController GetSEMController() { // get SEM, scanner and x-ray controller via hardware manager return m_SemController; } public CMsrThreadStatus GetMsrThreadStatus() { return m_ThreadStatus; } public void Init(COTSMsrPrjResultData a_pProjMgrFile) { m_pProjData = a_pProjMgrFile; m_listMeasurableSamples = a_pProjMgrFile.GetSampleList() ; return ; } void ThreadOver() { DateTime timeEnd = m_ThreadStatus.GetEndTime(); ST_MSTMsg MsrMsg = new ST_MSTMsg(); MsrMsg.iMsgType = ENUM_MSG_TYPE.MTHREADSTATUS; MsrMsg.STMThreadStu.iMsrStatu = m_ThreadStatus.GetStatus(); MsrMsg.STMThreadStu.csMsrEndTime = timeEnd.ToShortDateString(); ProgressEvent(MsrMsg); //disconnect the semcontroller when we exit this task. m_SemController.DisConnect(); } void SetWorkingFolderStr() { // get project file pathname string strSettingFilePathName = m_pProjData.GetPathName(); strSettingFilePathName.Trim(); if (strSettingFilePathName == "") { loger .Error("SetWorkingFolderStr: project file pathname is an empty string"); return ; } else if (strSettingFilePathName==UNTITLED_FILE_NAME) { loger .Error ("SetWorkingFolderStr: project file pathname is an invalid string"); return ; } // working folder string string strWorkingFolder =FileHelper.GetFolderName(strSettingFilePathName); // set working folder string m_strWorkingFolder = strWorkingFolder; // ok, return TRUE return ; } public void SendMessageToMeasureApp(ST_MSTMsg msg) { ProgressEvent(msg); } public bool IsMeasureStopped() { return m_ThreadStatus.GetStatus() == OTS_MSR_THREAD_STATUS.STOPPED; } public bool IsMeasureRunning() { return m_ThreadStatus.GetStatus() == OTS_MSR_THREAD_STATUS.INPROCESS; } public bool IsMeasureFailed() { return m_ThreadStatus.GetStatus() == OTS_MSR_THREAD_STATUS.FAILED; } public bool IsMeasureCompleted() { return m_ThreadStatus.GetStatus() == OTS_MSR_THREAD_STATUS.COMPLETED; } public void DoMeasure() { // start measurement, creat thread measure status class, let the main thread know that measurement started SortedDictionary mapSmplMsr=new SortedDictionary();//use this map to hold all the smplMeasure object m_ThreadStatus.SetStartTime(System.DateTime.Now); DateTime timeStart = m_ThreadStatus.GetStartTime(); ST_MSTMsg MsgMsrStart=new ST_MSTMsg(); MsgMsrStart.iMsgType = ENUM_MSG_TYPE.MTHREADSTATUS; MsgMsrStart.STMThreadStu.iMsrStatu = OTS_MSR_THREAD_STATUS.INPROCESS; MsgMsrStart.STMThreadStu.csMsrStartTime = timeStart.ToShortDateString(); ProgressEvent (MsgMsrStart); loger.Info("Measurement started!"); // connect hardware loger.Info("Connect SEM!"); if (!m_SemController.Connect()) { loger .Error("DoMeasure: failed to connect hardware."); m_ThreadStatus.SetStatus(OTS_MSR_THREAD_STATUS.FAILED); m_ThreadStatus.ComputeTime(OTS_THREAD_TIME_TYPE.STOPPED); ThreadOver(); return; } // set working directory which is the same directory of the setting file SetWorkingFolderStr(); List listMeasuredSamples = m_ThreadStatus.GetCompletedSamples(); // got through measure list foreach (var pSample in m_listMeasurableSamples) {// check and break if stop button is clicked if (m_ThreadStatus.GetStatus()== OTS_MSR_THREAD_STATUS.STOPPED ) { // stop button clicked loger .Info("DoMeasure: stop button is clicked."); // record end time m_ThreadStatus.ComputeTime(OTS_THREAD_TIME_TYPE.STOPPED); ThreadOver(); return; } if (!pSample.GetSwitch()) { continue; } CSmplMeasure pSmplMeasure; if (!mapSmplMsr.ContainsKey(pSample.GetName())) {// create a sample measure object for the sample switch (m_pProjData.m_nPackId) { case OTS_SysType_ID.IncA: pSmplMeasure = new CSmplMeasureInclution(m_strWorkingFolder, pSample); break; case OTS_SysType_ID.CleanlinessA: pSmplMeasure = new CSmplMeasureCleanliness(m_strWorkingFolder, pSample); break; default: pSmplMeasure = new CSmplMeasureInclution(m_strWorkingFolder, pSample); break; } // set measure thread pSmplMeasure.SetMsrThread(this); mapSmplMsr[pSample.GetName()] = pSmplMeasure; } else { pSmplMeasure = mapSmplMsr[pSample.GetName()]; pSample.GetMsrStatus().SetStatus(OTS_MSR_SAMPLE_STATUS.INPROCESS); m_ThreadStatus.SetStatus(OTS_MSR_THREAD_STATUS.INPROCESS); } pSmplMeasure.DoMeasureForOneSample(); // check if measurement is successful if (pSample.GetMsrStatus().GetStatus() == OTS_MSR_SAMPLE_STATUS.STOPPED) {// record end time m_ThreadStatus.SetStatus(OTS_MSR_THREAD_STATUS.STOPPED); m_ThreadStatus.ComputeTime(OTS_THREAD_TIME_TYPE.STOPPED); // update thread measure status class, let the main thread know that this sample measurement stopped ST_MSTMsg MsgSmpStop = new ST_MSTMsg(); MsgSmpStop.iMsgType = ENUM_MSG_TYPE.MSAMPLESTATUS; MsgSmpStop.STMThreadStu.iMsrStatu= OTS_MSR_THREAD_STATUS.STOPPED; MsgSmpStop.STMSampleStu.iMsrSampleStatu = OTS_MSR_SAMPLE_STATUS.STOPPED; MsgSmpStop.STMThreadStu.csMsrEndTime = DateTime .Now .ToShortDateString(); MsgSmpStop.STMThreadStu.iMsrStatu= OTS_MSR_THREAD_STATUS.STOPPED; ProgressEvent(MsgSmpStop); ThreadOver(); return; } else if (pSample.GetMsrStatus().GetStatus() == OTS_MSR_SAMPLE_STATUS.FAILED) { // measurement failed m_ThreadStatus.SetStatus(OTS_MSR_THREAD_STATUS.FAILED); // record end time m_ThreadStatus.ComputeTime(OTS_THREAD_TIME_TYPE.STOPPED); // update thread measure status class, let the main thread know that this sample measurement failed ST_MSTMsg MsgSmpFailed=new ST_MSTMsg(); MsgSmpFailed.iMsgType = ENUM_MSG_TYPE.MSAMPLESTATUS; MsgSmpFailed.STMSampleStu.iMsrSampleStatu = OTS_MSR_SAMPLE_STATUS.FAILED; ProgressEvent(MsgSmpFailed); ThreadOver(); return; } // record end time m_ThreadStatus.ComputeTime(OTS_THREAD_TIME_TYPE.STOPPED); // update thread measure status class, let the main thread know that this sample measurement successes ST_MSTMsg MsgSmpSuccess=new ST_MSTMsg(); MsgSmpSuccess.iMsgType = ENUM_MSG_TYPE.MSAMPLESTATUS; MsgSmpSuccess.STMSampleStu.iMsrSampleStatu = OTS_MSR_SAMPLE_STATUS.SUCCESSED; ProgressEvent(MsgSmpSuccess); // continue to the next sample listMeasuredSamples.Add (pSample.GetName()); } // measurement completed m_ThreadStatus.SetStatus(OTS_MSR_THREAD_STATUS.COMPLETED); // record end time m_ThreadStatus.ComputeTime(OTS_THREAD_TIME_TYPE.STOPPED); ThreadOver(); } // hole preview public void DoHolePreview(int a_nHoleID, CDomain a_pMeasureArea) { // start measurement, creat thread measure status class, let the main thread know that measurement started // set measure status to in-process //record time m_ThreadStatus.ComputeTime(OTS_THREAD_TIME_TYPE.START); DateTime timeStart = m_ThreadStatus.GetStartTime(); ST_MSTMsg MsgMsrStart = new ST_MSTMsg(); MsgMsrStart.iMsgType = ENUM_MSG_TYPE.MTHREADSTATUS; MsgMsrStart.STMThreadStu.iMsrStatu =OTS_MSR_THREAD_STATUS.INPROCESS; SendMessageToMeasureApp(MsgMsrStart); // connect hardware if (!m_SemController.Connect()) { // failed to connect hardware SetMsrLoopStatus(otsdataconst.OTS_MSR_THREAD_STATUS.FAILED); m_ThreadStatus.ComputeTime(OTS_THREAD_TIME_TYPE.STOPPED); ThreadOver(); } COTSSample pSampleHole = CreateHoleSample(a_pMeasureArea); // create a sample measure object for the sample CSmplMeasure pSmplMeasure = new CSmplMeasureCleanliness(m_strWorkingFolder,pSampleHole); // set measure thread pSmplMeasure.SetMsrThread(this); // update thread measure status class, let the main thread know that this sample measurement starts // set working folder string pSmplMeasure.SetSample(pSampleHole); pSmplMeasure.SetWorkingFolder(m_strWorkingFolder); // do measure pSmplMeasure.DoHolePreview(a_nHoleID); // check if measurement is successful if (pSampleHole.GetMsrStatus().GetStatus() == OTS_MSR_SAMPLE_STATUS.STOPPED) { // record end time m_ThreadStatus.ComputeTime(OTS_THREAD_TIME_TYPE.STOPPED); // measurement stopped SetMsrLoopStatus(otsdataconst.OTS_MSR_THREAD_STATUS.STOPPED); // update thread measure status class, let the main thread know that this sample measurement stopped ST_MSTMsg MsgSmpStop = new ST_MSTMsg(); MsgSmpStop.iMsgType = ENUM_MSG_TYPE.MSAMPLESTATUS; MsgSmpStop.STMSampleStu.iMsrSampleStatu = OTS_MSR_SAMPLE_STATUS.STOPPED; SendMessageToMeasureApp(MsgSmpStop); ThreadOver(); return; } else if (pSampleHole.GetMsrStatus().GetStatus() == OTS_MSR_SAMPLE_STATUS.FAILED) { // measurement failed SetMsrLoopStatus(otsdataconst.OTS_MSR_THREAD_STATUS.FAILED); // record end time m_ThreadStatus.ComputeTime(OTS_THREAD_TIME_TYPE.STOPPED); // update thread measure status class, let the main thread know that this sample measurement failed ST_MSTMsg MsgSmpFailed = new ST_MSTMsg(); MsgSmpFailed.iMsgType = ENUM_MSG_TYPE.MSAMPLESTATUS; MsgSmpFailed.STMSampleStu.iMsrSampleStatu = OTS_MSR_SAMPLE_STATUS.FAILED; SendMessageToMeasureApp(MsgSmpFailed); ThreadOver(); return; } // record end time m_ThreadStatus.ComputeTime(OTS_THREAD_TIME_TYPE.STOPPED); // update thread measure status class, let the main thread know that this sample measurement successes ST_MSTMsg MsgSmpSuccess = new ST_MSTMsg(); MsgSmpSuccess.iMsgType = ENUM_MSG_TYPE.MSAMPLESTATUS; MsgSmpSuccess.STMSampleStu.iMsrSampleStatu = OTS_MSR_SAMPLE_STATUS.SUCCESSED; SendMessageToMeasureApp(MsgSmpSuccess); // measurement completed SetMsrLoopStatus(otsdataconst.OTS_MSR_THREAD_STATUS.COMPLETED); // record end time m_ThreadStatus.ComputeTime(OTS_THREAD_TIME_TYPE.STOPPED); ThreadOver(); } public COTSSample CreateHoleSample(CDomain a_pMsrArea) { COTSSample pHoleSample = new COTSSample(); pHoleSample.SetMsrArea(a_pMsrArea); // get min magnification CSEMStageData pSEMStageData = m_pProjData.GetSEMStageData(); double dMinMag = pSEMStageData.GetMinMag(); // get scan field size 100 int nScanFieldSize100 = pSEMStageData.GetScanFieldSize100(); // get working distance double dWorkingDistance = 0.0; if (!GetSEMWorkingDistanceFromHW(ref dWorkingDistance)) { return null; } CSEMDataMsr poSEMDataMsr = new CSEMDataMsr(); poSEMDataMsr.SetScanFieldSize100(nScanFieldSize100); poSEMDataMsr.SetWorkingDistance(dWorkingDistance); poSEMDataMsr.SetMagnification(dMinMag); pHoleSample.SetSEMDataMsr(poSEMDataMsr); // Set image scan param COTSImgScanPrm poImageScanParam = new COTSImgScanPrm(); poImageScanParam.SetStopMode(OTS_MEASURE_STOP_MODE.CoverMode); poImageScanParam.SetStartImageMode(OTS_GET_IMAGE_MODE.FROM_CENTER); poImageScanParam.SetScanImageSpeed(OTS_THREE_TIES_OPTIONS.low); //poImageScanParam.SetImagePixelSize(OTS_FIVE_TIES_OPTIONS.TIE1); CSampleParam poMsrParams = pHoleSample.GetMsrParams(); poImageScanParam.SetImagePixelSize(m_listMeasurableSamples[0].GetMsrParams().GetImageScanParam().GetImagePixelSize());//由于各样品分辨率应该一致,故此处没有读取选取的特定样品孔样品 poMsrParams.SetImageScanParam(poImageScanParam); pHoleSample.SetMsrParams(poMsrParams); return pHoleSample; } public bool GetSEMWorkingDistanceFromHW(ref double a_dWorkingDistance) { m_SemController.GetWorkingDistance(ref a_dWorkingDistance); return true; } // measure status public void SetMsrLoopStatus(otsdataconst.OTS_MSR_THREAD_STATUS a_nMsrLoopStatus) { if (a_nMsrLoopStatus >= otsdataconst.OTS_MSR_THREAD_STATUS.MIN && a_nMsrLoopStatus <= otsdataconst.OTS_MSR_THREAD_STATUS.MAX) { m_ThreadStatus.SetStatus( a_nMsrLoopStatus); } } } }