//时间:20200610 //作者:郝爽 //功能:测量线程 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Threading; using System.IO; using System.Drawing; using System.Xml; using SmartSEMControl; using MeasureData; using FileManager; using WebManager; using OpenCvSharp; using DBManager; using Extender; using CookComputing.XmlRpc; namespace MeasureThread { #region 信息定义 public delegate void ThreadStatusHandler(object sender, ThreadStatusEventArgs e); //声明委托 public delegate void CutHolesStatusHandler(object sender, CutHolesStatusEventArgs e); //测量线程状态 public class ThreadStatusEventArgs { //状态 public Boolean State { get { return m_state; } set { m_state = value; } } private Boolean m_state; //时间 public DateTime Time { get { return m_time; } set { m_time = value; } } private DateTime m_time; //步骤代码 public String Step_Code { get { return step_Code; } set { step_Code = value; } } private String step_Code; //其他消息 public String Message { get { return this.message; } set { this.message = value; } } private String message; //孔名 public String HoleName { get { return this.holeName; } set { this.holeName = value; } } private String holeName; //图片信息 public class PictureInformation { public String Picture_FullPath { get { return picture_FullPath; } set { picture_FullPath = value; } } private String picture_FullPath; public double Work_Voltage { get { return work_Voltage; } set { work_Voltage = value; } } private double work_Voltage; public double Magnification { get { return magnification; } set { magnification = value; } } private double magnification; public double Work_Distance { get { return work_Distance; } set { work_Distance = value; } } private double work_Distance; public String Work_Status { get { return work_Status; } set { work_Status = value; } } private String work_Status; } //图片信息实例类 public PictureInformation Picture_Information { get { return picture_Information; } set { picture_Information = value; } } private PictureInformation picture_Information; //图像处理信息 public class ImageInformation { public int Method_Name { get { return method_Name; } set { method_Name = value; } } private int method_Name; public Source_Img_Degree_Direction SIDD; public Cut_Position CP; public Cut_Success CS; public Trapezoid_Top_Center_Position TTCP; public Auto_Foucs AF; public Auto_Stigmatic AS; public Center_Position_OffsetAngle_Direction CPOD; public Measure_Size MS; public ImageInformation() { SIDD = new Source_Img_Degree_Direction(); CP = new Cut_Position(); CS = new Cut_Success(); TTCP = new Trapezoid_Top_Center_Position(); AF = new Auto_Foucs(); AS = new Auto_Stigmatic(); CPOD = new Center_Position_OffsetAngle_Direction(); MS = new Measure_Size(); } //1-计算原始图像偏移角度及方向 public class Source_Img_Degree_Direction { public Boolean Is_Image = false; public double Degree { get { return degree; } set { degree = value; } } private double degree; public int Direction { get { return direction; } set { direction = value; } } private int direction; public int State { get { return state; } set { state = value; } } private int state; } //2-计算切割点位置 public class Cut_Position { public Boolean Is_Image = false; public double Offsetx { get { return offsetx; } set { offsetx = value; } } private double offsetx; public double Offsety { get { return offsety; } set { offsety = value; } } private double offsety; public int State { get { return state; } set { state = value; } } private int state; } //3-是否切割成功 public class Cut_Success { public Boolean Is_Image = false; public int State { get { return state; } set { state = value; } } private int state; } //4-计算切割后图像梯形区域上边中心点坐标 public class Trapezoid_Top_Center_Position { public Boolean Is_Image = false; public double TopCenterX { get { return topCenterX; } set { topCenterX = value; } } private double topCenterX; public double TopCenterY { get { return topCenterY; } set { topCenterY = value; } } private double topCenterY; public int State { get { return state; } set { state = value; } } private int state; } //5-自动对焦 public class Auto_Foucs { public Boolean Is_Image = true; public String Img_Path { get { return img_Path; } set { img_Path = value; } } private String img_Path; } //6-自动像闪 public class Auto_Stigmatic { public Boolean Is_Image = true; public String Img_Path { get { return img_Path; } set { img_Path = value; } } private String img_Path; } //7-计算切割面区域中心坐标,以及偏移角度及方向 public class Center_Position_OffsetAngle_Direction { public Boolean Is_Image = false; public double CenterX { get { return centerX; } set { centerX = value; } } private double centerX; public double CenterY { get { return centerY; } set { centerY = value; } } private double centerY; public double Degree { get { return degree; } set { degree = value; } } private double degree; public int Direction { get { return direction; } set { direction = value; } } private int direction; public int State { get { return state; } set { state = value; } } private int state; } //8-测量尺寸 public class Measure_Size { public Boolean Is_Image = false; public int State { get { return state; } set { state = value; } } private int state; } } //图像处理信息实例类 public ImageInformation Image_Information { get { return image_Information; } set { image_Information = value; } } private ImageInformation image_Information; public ThreadStatusEventArgs(string a_State) { picture_Information = new PictureInformation(); image_Information = new ImageInformation(); } } //切孔状态 public class CutHolesStatusEventArgs { public string State { get { return m_state; } set { m_state = value; } } private string m_state; public string HoleName { get { return m_holeName; } set { m_holeName = value; } } private string m_holeName; public CutHolesStatusEventArgs(string a_state, string a_holeName) { this.m_state = a_state; this.m_holeName = a_holeName; } } #endregion public class Measure { NLog.Logger log; #region 变量定义 //Web接口类 public ImageProcess ImagePro = null; //SmartSEM远程路径 public String RemoteELYPath = ""; public String RemoteMLFPath = ""; //扫描周期 private float cycle_time = 0; //人工干预 public int hand_intervene = 2; //样品台保护值 public float X_Min = 0; public float X_Max = 0.13f; public float Y_Min = 0; public float Y_Max = 0.13f; public float Z_Min = 0; public float Z_Max = 0.05f; public float T_Min = -4; public float T_Max = 70; public float R_Min = -380; public float R_Max = 380; public float M_Min = 0; public float M_Max = 0.012f; public event ThreadStatusHandler SendThreadStatus; // 声明事件 public event CutHolesStatusHandler SendCutHolesStatus; // 声明事件 //定义一个全局的消息类 ThreadStatusEventArgs arg = new ThreadStatusEventArgs("0-0"); //全局只有一个fatorySEM static FactoryHardware factorySEM = FactoryHardware.Instance; ISEMControl iSEM = factorySEM.ISEM; //@的作用是不用转义字符\\只打一个就行 const String ImageName0 = @"SEMStraighten.tif"; //传给客户,原始图像,为拉直操作 const String ImageName1 = @"FIBStraighten.tif"; //传给客户,原始图像,为拉直操作 //const String ImageName1 = @"SEMFindCutPostion.tif"; //传给客户,作水平校正 const String ImageName2 = @"FIBCutPostion.tif";//传给客户,找到切割点 const String ImageName31 = @"FIBBefore.tif"; const String ImageName32 = @"FIBAfter.tif"; const String ImageName4 = @"SEMTrapCP.tif";//传给客户,找到已经切割点 const String ImageName5 = @"SEMDegreeTrap.tif";//传给客户,水平校正 const String ImageName6 = @"SEMMagEnd.tif";//传给客户,测量层高 const String ImageName7 = @"BeforeImage.tif";//计算感兴趣的区域 const String ImageNameTwo_1 = @"SEMRegion.tif";//第一个测量区域拍照 const String MacoInsertPt = "GIS Insert.MLF"; //传入PT针 const String MacoRetractPt = "GIS Retract.MLF"; //退出PT针 const String MacoScanPic = "Scan picture.MLF";//一般质量抓图的宏 const String MacoGoodPic = "Good picture.MLF";//高质量抓图的宏 const String MacoExportTif = "Export TIFF.MLF";// 导出有标尺的图像,这个图像为使用工具SmartSEM工具的图像 const String ElyDeposition = @"Deposition.ely"; //沉积 const String ElyCrossSection = @"CrossSection.ely"; //切割 const float fMin = (float)0.0000002; //单位是米 private Locate m_locate; private Focus m_focuse; private Photo m_photo; private ArgSend mArgSend; public Boolean key_stop = false; FibWork m_FibWork; PTWork m_PTWork; //int m_nWorkHoleNo = -1; public ThreadStatusEventArgs GetArgs() { return arg; } //测量文件 private MeasureFile m_measureFile; public MeasureFile MeasureFile { get { return this.m_measureFile; } set { this.m_measureFile = value; } } //测量的切割孔,这个切孔的状态要保存的测量文件中去 private List m_cutHoles; public List cutHoles { get { return this.m_cutHoles; } set { this.m_cutHoles = value; } } public MeasureParam MParam//测量参数对象 { get { return this.m_measureFile.MParam; } //set { this.m_measureParam = value; } } //工作文件夹 private string m_WorkingFolder; public string mWorkingFolder { get { return this.m_WorkingFolder; } set { this.m_WorkingFolder = value; } } //程序当前路径 private string m_ProgramFolder = Directory.GetCurrentDirectory(); //线程状态 private ThreadStatus m_ThreadStatus; public ThreadStatus TStatus { get { return this.m_ThreadStatus; } set { this.m_ThreadStatus = value; } } #endregion //构造函数 public Measure(String webServerIP, String webServerPort, String webServerUrl, MeasureFile a_measurefile) { m_measureFile = a_measurefile; //SmartSEM远程路径 log = NLog.LogManager.GetCurrentClassLogger(); TStatus = new ThreadStatus(); cutHoles = new List(); //配置远程连接 ImagePro = new ImageProcess(webServerIP, webServerPort, webServerUrl); m_locate = new Locate(iSEM); m_focuse = new Focus(iSEM ,MParam.FocusServerIP,MParam.FocusServerPort); m_photo = new Photo(iSEM); mArgSend = new ArgSend(this, iSEM); } //初始化测量业务, 读测量文件,判断是否有可测试的切孔 public bool InitMeasure() { log.Info("进入初始化函数"); List listHoles = m_measureFile.ListCutHole; foreach (CutHole h in listHoles) { if (h.SWITCH == true) { m_cutHoles.Add(h); } } if (m_cutHoles.Count == 0) return false; if (MParam.IsEDSToRun == true) { log.Info("要进行能谱分析,初始化Xray数据库", true); Init_MeasDB(); } //设置工作文件夹 mWorkingFolder = m_measureFile.FilePath; for(int i=0;i ///13. 自动 定位功能,沉积 /// /// public bool GetFIBPosition(CutHole currHole) { float px = 0; int state = 1; log.Info("测量线程:FIB自动定位放大倍数" + MParam.Location_Magnification.ToString(), true); Thread.Sleep(200); if (!m_focuse.SetMagnification(MParam.Location_Magnification))// the m_focuse object is used to set magnification. { log.Error("测量线程报错:13.自动定位、沉积-自动定位放大倍数" + MParam.Location_Magnification.ToString() + "控制失败", false); return false; } arg.State = true; arg.Message = "放大" + MParam.Location_Magnification.ToString("0.0") + "倍成功"; SendMsg("2-0"); //判断是否停止进程 if (key_stop) { log.Info("停止键按下。", true); return false; } log.Info("测量线程:切换到FIB模式。", true); //6.设置FIB拍照参数——扫描时间、束流等 //7.控制FIB自动亮度、对比度 Thread.Sleep(200); if (!iSEM.CmdFIBModeFIB()) { log.Error("测量线程报错:切换到FIB模式失败。", false); return false; } arg.State = true; arg.Picture_Information.Work_Status = "FIB"; arg.Message = "FIB模式切换成功"; SendMsg("2-1"); Thread.Sleep(1000); //判断是否停止进程 if (key_stop) { log.Info("停止键按下。", true); return false; } //刷亮度对比度 iSEM.CmdFocusScanSpeed("CMD_SCANRATE4");//iSEM.CmdSaveRate(); Thread.Sleep(200); iSEM.SetAutoBright(100); Thread.Sleep(200); iSEM.SetAutoContrast(100); iSEM.SetAutoVideoBrightnessAndContrast(); Thread.Sleep(10000); iSEM.SetAutoVideoOff(); //8.控制FIB拍照 Thread.Sleep(200); String fileName2 = currHole.data_path + "\\" + currHole.HoleName + "_" + MParam.Location_Magnification.ToString("0") + "_" + ImageName2; m_photo.GetParam().Mag = MParam.Location_Magnification; m_photo.GetParam().savePath = fileName2; if (!m_photo.TakePhoto()) { log.Error("测量线程报错:FIB拍照失败。", false); return false; } mArgSend.SendArgumentToScreen("2-3", "FIB拍照成功",fileName2); //判断是否停止进程 if (key_stop) { log.Info("停止键按下。", true); return false; } float x1=0,y1=0, x2=0, y2=0; //10.将照片传给客户,返回梯形位置坐标,及样品类型参数(是否需要PT沉积,PT坐标位置,PT宽度、PT高度、梯形上、下边及深度、扫描时间、束流、样品放大倍数1、样品放大倍数2等切割参数) log.Info("测量线程:调用图像处理接口,准备移动样品台", true); log.Info("文件名=" + fileName2, true); log.Info("样品类型=" + MParam.SampleType, true); log.Info("供应商名称=" + MParam.Firm, true); ImagePro.Img_Cut_Position(fileName2, Convert.ToInt32(MParam.SampleTypeNo()), MParam.Firm, out x1, out y1, out x2, out y2, out state); log.Info("FIB梯形左上角和右上角位置信息= (" + x1.ToString() + ", " + y1.ToString() + "), (" + x2.ToString() + ", " + y2.ToString() + ")", true); log.Info("准备移动样品台返回状态=" + state.ToString(), true); if (state == 1) { m_FibWork.ModifyCutTemplate(x1, y1 ,x2, y2); //判断是否需要PT沉积,如果需要也要修改PT的ELY文件 if (MParam.IfPT) { m_PTWork.ModifyPTTemp(x1,y1,x2,y2); } } else { log.Error("测量线程报错:自动定位图像接口返回0", false); return false; } iSEM.CmdFIBModeSEM(); Thread.Sleep(200); if (key_stop) { log.Info("停止键按下", true); return false; } return true; } /// /// 14. FIB切割 /// /// public bool FIBCuting() { int state = 0; if (!iSEM.CmdFIBModeFIB()) { log.Error("测量线程报错:切换到SEM模式失败", false); return false; } //arg.Message = "切换到FIB模式成功。"; //arg.State = true; //SendMsg("2-7"); Thread.Sleep(1000); m_focuse.SetMagnification(MParam.Location_Magnification); //14.自动控制FIB切割 //1.根据参数设置FIB草率时间(使图清晰),设置梯形上下边及深度、设置束流 //2.控制FIB进行切割 //以上1、2步全部用ELY文件代替 if (!m_FibWork.DoFIBWork()) { log.Error("测量线程报错:" + arg.Message, false); return false; } arg.State = true; arg.Message = "FIB切割成功"; SendMsg("2-8"); Thread.Sleep(300); //判断是否停止进程 if (key_stop) { log.Info("停止键按下", true); return false; } //做切割后SEM的拍照 log.Info("测量线程:切割后SEM拍照", true); if (!iSEM.CmdFIBModeSEM()) { log.Error("测量线程报错:切换到SEM模式失败", false); return false; } Thread.Sleep(1000); mArgSend.SendArgumentToScreen("3-0", "SEM模式切换成功"); return true; } /// /// 16.自动调整SEM找到切割位置,这里不是简单的拉直旋转 /// /// public bool MoveToCenter(CutHole currHole) { float x2 = 0, y2 = 0; int state = 0; //切换到SEM模式 if (!iSEM.CmdFIBModeSEM()) { log.Error("测量线程报错:" + arg.Message, false); return false; } Thread.Sleep(1000); if (key_stop) { log.Info("停止键按下", true); return false; } String fileName4 = currHole.data_path + "\\" + MParam.Firm + "-" + MParam.MoveToCenterMagnification.ToString("0") + "-" +currHole.HoleName + "-" + ImageName4; //add by zjx 2020-12-21 根据客户的需求修改图片的名称 end log.Info("fileName4=" + fileName4, true); float mag = MParam.MoveToCenterMagnification; m_photo.GetParam().Mag = mag; m_photo.GetParam().savePath = fileName4; m_photo.TakePhoto(); //arg.Message = "SEM拍照成功"; mArgSend.SendArgumentToScreen("3-2", "SEM拍照成功", fileName4); if (key_stop) { return false; } //4.将照片传给客户,获取偏移坐标,以及偏移角度 //5.根据坐标控制SEM移动到切孔位置,居中 log.Info("测量线程:图像处理接口,准备移动样品台", true); log.Info("文件名=" + fileName4, true); ImagePro.Img_Trapezoid_Top_Center_Position(fileName4, Convert.ToInt32(MParam.SampleTypeNo()), MParam.Firm, out x2, out y2, out state); log.Info("梯形中心点返回数据=" + x2.ToString() + ", " + y2.ToString(), true); log.Info("准备移动样品台返回状态=" + state.ToString(), true); if (state == 1) { //1. 根据客户PT沉积坐标控制FIB调整到中心位置??????? if (!m_locate.MoveToPixByMoveStage(x2, y2)) { log.Error("测量线程报错:" + arg.Message, false); return false; } arg.Message = "移动到新(" + x2.ToString() + "," + y2.ToString() + ")位置成功"; mArgSend.SendArgumentToScreen("3-3",arg.Message); //add by zjx 2020-12-21 根据客户的需求修改图片的名称 // String fileName10 = currHole.data_path + "\\" + MParam.Firm + "-" + MParam.Location_Magnification.ToString("0") + "-" + currHole.HoleName + "-" + ImageName10; //add by zjx 2020-12-21 根据客户的需求修改图片的名称 end //log.Info("fileName10====================" + fileName10, true); //m_photo.GetParam().savePath = fileName10; } else { log.Error("测量线程报错:" + arg.Message, false); return false; } //判断是否停止进程 return true; } /// /// 17.自动控制SEM拍截面照 /// /// public bool ShotSection(CutHole currHole,ref List limg_path, ref List lsize) { int state = 0; #region 对焦 Focusparam fprm = new Focusparam() { //voltage = MParam.Photograph_Voltage, mag = MParam.Photograph_Magnification, reduceWindowPos = new System.Drawing.Point(402, 128), reduceWinHeight = 600, reduceWinWidth = 600, IfAutoBrightnessAndContrast = true, brightness = 50.0f, contrast = 30.0f, tiltCorrAngle = MParam.Correction_Angle_Inside, workingDis = 5.0f, }; //add by sun 2020-12-17 解决3大类样品FIB下亮度过白导致无法识别问题 float m_TempBrightness=MParam.ShotSectionManualBrightness; float m_TempContrast = MParam.ShotSectionManualConstrast; fprm.brightness = m_TempBrightness; fprm.contrast = m_TempContrast; m_focuse.setFocusParam(fprm); m_focuse.DoFocusBySetMainParameter(); Thread.Sleep(200); if (key_stop) { log.Info("停止键按下", true); return false; } //4、拍照,5、保存照片 string fileName6 =currHole.data_path + "\\" + MParam.Firm + "-" + MParam.Photograph_Magnification.ToString("0") + "-" + currHole.HoleName + "-" + ImageName6; log.Info("fileName6=" + fileName6, true); PhotoParam phop = new PhotoParam() { Mag = MParam.Photograph_Magnification, savePath=fileName6, }; m_photo.SetPhotoParam(phop); if (!m_photo.TakePhoto()) { log.Error("测量线程报错:" + arg.Message, false); return false; } mArgSend.SendArgumentToScreen("3-7", "SEM拍照成功", fileName6); //判断是否停止进程 if (key_stop) { log.Info("停止键按下", true); return false; } #endregion #region 获取两个位置上及放大倍数 //计算两个测量区域坐标 List ct = new List(); List mag = new List(); System.Drawing.Point ct0; float magMax; //log.Info("测量线程:图像处理接口,计算两个放大区域坐标,输入图像" + fileName6.ToString() + "控制失败", true); ImagePro.Img_Measure_Region_Position(fileName6, Convert.ToInt32(MParam.SampleTypeNo()), MParam.Firm, out ct, out mag, out ct0, out state); string s = ""; for (int i = 0; i < ct.Count; i++) { s += " 输出观测点" + i.ToString() + "为(" + ct[i].X.ToString() + "," + ct[i].Y.ToString() + ")" + "放大倍数=" + mag[i]; } log.Info("测量线程:图像处理接口返回值为" + state.ToString() + ",计算放大区域坐标,输入图像" + fileName6.ToString() + "输入样品 " + Convert.ToInt32(MParam.SampleTypeNo()) + " 输入厂商" + MParam.Firm.ToString() + s + " 输出对焦点为(" + ct0.X.ToString() + "," + ct0.Y.ToString() + ")", true); #endregion if (state == 1 && ct.Count != 0 && mag.Count != 0) { // mArgSend.SendArgumentToScreen("3-10", "获取位置及放大倍数成功"); #region 记录初始设置的BeamShift的百分比和样品台XY位置 m_locate.RecordCurrentBeamShiftAndStagePosition(); #endregion //找到放大倍数最大值 magMax = mag[0]; for (int m = 1; m < mag.Count; m++) { if (mag[m] > magMax) { magMax = mag[m]; } } #region 移动到梯形左上角 //20201127将视野移动到梯形左上角位置 m_locate.MoveToPix(ct0.X, ct0.Y,MParam.PixelSizeCor); log.Info("测量线程:移动到对焦点( " + ct0.X.ToString() + "," + ct0.Y.ToString() + ")", true); Thread.Sleep(200); //循环升级对焦,每次放大3000倍 int startmag=7000; if (magMax < 7000) { startmag =(int) magMax; } for (int curmag= startmag; curmag < magMax+3000; curmag += 5000) { fprm.IfAutoBrightnessAndContrast = true; fprm.reduceWinHeight = 480; fprm.reduceWinWidth = 450; fprm.tiltCorrAngle = MParam.Correction_Angle_Inside; fprm.mag = curmag; m_focuse.setFocusParam(fprm); m_focuse.DoFocusBySetMainParameter(); } #endregion m_photo.EnforceContrast() ;//enter inside,enforce the brightness. #region 循环拍照 for (int n = 0; n < ct.Count; n++) { #region 恢复到拍照的状态 m_locate.RestoreLastBeamShiftAndStagePosition(); #endregion #region 移动到指定点位置 LocateParam lprm = new LocateParam() { PositionX = ct[n].X, PositionY = ct[n].Y, PixelSize_Y_cur = MParam.PixelSizeCor, }; if (!m_locate.MoveToPix(lprm.PositionX,lprm.PositionY,lprm.PixelSize_Y_cur)) { //移动到第一个点失败 log.Error("测量线程报错:移动到点(" + ct[n].X.ToString() + "," + ct[n].Y.ToString() + ")失败", false); } log.Info("测量线程:移动到第1个点( " + ct[n].X.ToString() + "," + ct[n].Y.ToString() + ")", true); #endregion log.Info("测量线程:移动到第一个点位置完成!", true); #region 拍照 //add by zjx 2020-12-21 根据客户的需求修改图片的名称 String fileName7 = currHole.data_path + "\\" + MParam.Firm + "-" + mag[n].ToString() + "-" + currHole.HoleName + "-" + n.ToString() + "-" + ImageNameTwo_1; //add by zjx 2020-12-21 根据客户的需求修改图片的名称 end log.Info("fileName7=" + fileName7, true); if (n == 0)//如果是第一个观测点就做角度修正 { String fileName5 = currHole.data_path + "\\" + MParam.Firm + "-" + mag[n].ToString("0") + "-" + currHole.HoleName + "-" + ImageName5; //add by zjx 2020-12-21 根据客户的需求修改图片的名称 end log.Info("纠正前 fileName5=" + fileName5, true); m_photo.GetParam().savePath = fileName5; m_photo.GetParam().Mag = mag[n]; m_photo.TakePhoto(); //8,计算切割面区域偏移角度及方向 float degree = 0; int direction = 0; ImagePro.Img_Center_Position_OffsetAngle_Direction(fileName5, Convert.ToInt32(MParam.SampleTypeNo()), MParam.Firm, out degree, out direction, out state); log.Info("测量线程:拍照角度移动,输入图像" + fileName5 + "输出角度为" + degree.ToString() + "输出方向为" + direction.ToString() + "返回值为" + state.ToString(), true); log.Info("样品" + currHole.HoleNo.ToString() + "计算切割面区域的角度=" + degree.ToString(), true); //接口返回像素,*pixelsize,得到坐标点。判断移动方式 if (state == 1) { //梯形角度 iSEM.SetScanRotationOn(); Thread.Sleep(200); //20201128陈工要求,谭博返回角度*0.7 iSEM.SetScanRotation(Convert.ToSingle(degree * MParam.ScanRotCor)); Thread.Sleep(200); string Message = "图像接口返回角度为:" + degree.ToString(); mArgSend.SendArgumentToScreen("3-8", Message, fileName5); } else { log.Error("测量线程报错:" + arg.Message, false); } //纠正后再次拍照 log.Info("纠正后拍照!"); m_photo.GetParam().savePath = fileName7; m_photo.GetParam().Mag = mag[n]; m_photo.TakePhoto(); } else { m_photo.GetParam().savePath = fileName7; m_photo.GetParam().Mag = mag[n]; m_photo.TakePhoto(); } iSEM.CmdFocusScanSpeed("CMD_SCANRATE4");//CmdFocusRate(4); Thread.Sleep(200); cycle_time = iSEM.GetCycleTime(); Thread.Sleep(Convert.ToInt32(cycle_time) + 100); Thread.Sleep(200); mArgSend.SendArgumentToScreen("3-11", "移动到第"+n+"个点位置拍照成功",fileName7); // add by zjx 在测量图上显示放大倍数和像素大小等信息 limg_path.Add(fileName7); //获取像素大小 lsize.Add(iSEM.GetPixelSize()); float size = iSEM.GetPixelSize(); SaveMeasDataPic(fileName7, mag[n], size); Thread.Sleep(1000); #endregion } #endregion } else { log.Error("测量线程报错:" + arg.Message, false); } //判断是否停止进程 if (key_stop) { log.Info("停止键按下", true); return false; } iSEM.SetTiltAngleOff(); Thread.Sleep(200); iSEM.SetScanRotationOff(); Thread.Sleep(200); iSEM.SetBeamShiftX(0); Thread.Sleep(200); iSEM.SetBeamShiftY(0); Thread.Sleep(200); //判断是否停止进程 if (key_stop) { return false; } return true; } public bool HoleMeasure(CutHole currHole) { //第一步:位置恢复 #region 第一步:坐标恢复 mArgSend.SendArgumentToScreen("0-0","");// tell the HMI that we are doing this step. log.Info("测量线程:恢复样品分析点位置(R,X,Y,Z) = (" + currHole.Position.R.ToString() + "," + currHole.Position.X.ToString() + "," + currHole.Position.Y.ToString() + "," + currHole.Position.Z.ToString() + ")", true); Locate lo = new Locate(iSEM); lo.LocateCutHolePosition(currHole, MParam.IfTilt); if (!m_focuse.SetMagnification(MParam.Location_Magnification)) { log.Error("测量线程报错:设置定位放大倍数" + MParam.Location_Magnification.ToString() + "控制失败", false); return false; } if (key_stop) { log.Info("停止键被按下", true); return false; } arg.HoleName = currHole.HoleName; arg.State = true; arg.Message = "样品台移动到观测点:" + currHole.HoleName + "的位置成功。"; SendMsg("0-0"); #endregion //第二步:设置电流电压 //add by sun 2020-12-17 不同样品电压、电流不一致,应设置灵活。123456类电压2000V电流200pA,7类5000V 500pA,能谱 7000V 500pA log.Warn("测量线程:设置拍照电压 123456类电压2000V电流200pA,7类5000V 500pA,能谱 7000V 500pA 目前是第 " + MParam.SampleType + " 类!", true); if (!SetVoltageAndIPROBE()) { log.Error("测量线程:设置电压电流失败!目前是第 " + MParam.SampleType + " 类!", false); return false; } //第三步:自动化流程-每个点都需要补偿54度 if (MParam.IsTiltCorrectionToRun == true) { mArgSend.SendArgumentToScreen("0-1", "");// tell the HMI that we are doing this step. log.Info("测量线程:倾斜补偿角度为" + MParam.Correction_Angle_OutSide.ToString(), true); if (!m_focuse.TiltCorrection(MParam.Correction_Angle_OutSide)) { log.Error("测量线程报错:倾斜补偿角度为54度失败", false); return false; } arg.Message = "角度补偿54度成功!"; arg.State = true; SendMsg("0-1"); } //判断是否停止进程 if (key_stop) { log.Info("停止键按下", true); return false; } //第四步:拉直 ( 2是5/6类样品,不需要拉直) if (MParam.IsStraightenToRun) { mArgSend.SendArgumentToScreen("1-0", "");// tell the HMI that we are doing this step. log.Warn("测量线程:拉直操作开始!", true); if (!Straighten_Handle(currHole)) { log.Info("测量线程报错:拉直操作失败。", true); return false; } log.Info("测量线程:拉直操作结束!", true); } else { arg.Message = "拉直操作自动对焦成功!"; arg.State = true; SendMsg("1-0"); Thread.Sleep(200); arg.Message = "拉直拍照成功!"; arg.Picture_Information.Picture_FullPath = ""; arg.State = true; SendMsg("1-1"); Thread.Sleep(200); arg.Message = "拉直操作完成"; arg.State = true; SendMsg("1-2"); Thread.Sleep(200); } //判断是否停止进程 if (key_stop) { log.Info("停止键按下。", true); return false; } //第五步: 定位功能 PT沉积 if (MParam.IsGetFibPositionToRun==true) { mArgSend.SendArgumentToScreen("2-0", "");// tell the HMI that we are doing this step. log.Warn("测量线程:PIB切割自动定位开始!", true); if (!GetFIBPosition(currHole)) { log.Error("测量线程报错:自动定位失败,程序退出。", false); return false; } log.Info("测量线程:自动定位结束!", true); } //11.自动工具样品类型参数确定是否需要PT沉积 log.Info("是否PT:" + MParam.IfPT.ToString()); if (MParam.IfPT) { arg.State = true; arg.Message = "PT沉积"; SendMsg("2-4"); m_PTWork.DoWholePTWork(); } arg.State = true; arg.Message = ""; SendMsg("2-5"); arg.State = true; arg.Message = ""; SendMsg("2-6"); //判断是否停止进程 if (key_stop) { log.Info("停止键按下。", true); return false; } //14.自动控制FIB切割 del by sun 2020-12-15 temp if (MParam.IsFibCutingToRun == true) { mArgSend.SendArgumentToScreen("2-8", "");// tell the HMI that we are doing this step. log.Warn("测量线程:自动控制FIB切割开始!==" + MParam.IsFibCutingToRun, true); if (!FIBCuting()) { log.Error("测量线程报错:自动控制FIB切割失败。", false); return false; } log.Info("测量线程:自动控制FIB切割结束!", true); } //del by sun 2020-12-15 temp end //判断是否停止进程 if (key_stop) { return false; } //16.找到切割位置 if (MParam.IsMoveToCenterToRun == true) { mArgSend.SendArgumentToScreen("3-2", "");// tell the HMI that we are doing this step. log.Info("测量线程:找到切割位置开始!移动到视野中心", true); if (!MoveToCenter(currHole)) { return false; } log.Info("测量线程:找到切割位置结束!", true); //判断是否停止进程 if (key_stop) { return false; } } List limg_path = new List(); List lsize = new List(); if (MParam.IsShotSectionToRun == true) { mArgSend.SendArgumentToScreen("3-7", "");// tell the HMI that we are doing this step. log.Warn("测量线程:自动控制SEM拍截面照开始", true); //将过程17最后的拍照图片提出给18步进行调用 //17.自动控制SEM拍截面照 if (!ShotSection(currHole, ref limg_path, ref lsize)) { log.Error("拍截面照失败!"); return false; } log.Info("测量线程:自动控制SEM拍截面照结束!", true); //18.自动层高分析 if (MParam.IsLayerAnalysisToRun == true) { mArgSend.SendArgumentToScreen("3-12", "");// tell the HMI that we are doing this step. log.Warn("测量线程:自动层高分析开始", true); float size = iSEM.GetPixelSize(); int state = 1; ImagePro.Img_Measure_Height(limg_path, lsize, currHole.data_path + "\\", Convert.ToInt32(MParam.SampleTypeNo()), MParam.Firm, out state); if (state == 0) { log.Error("层高分析失败!"); return false; } arg.State = true; arg.Message = "层高分析成功"; SendMsg("3-12"); log.Info("测量线程:自动层高分析结束!", true); } } //判断是否停止进程 if (key_stop) { return false; } //} //add by zjx 2020-12-18 为了测试只做能谱部分 end //19.能谱分析 if (MParam.IsEDSToRun) { mArgSend.SendArgumentToScreen("4-0", "");// tell the HMI that we are doing this step. if (currHole.HoleNo + 1 == m_cutHoles.Count)//最后样品分析点才跑能谱 { log.Warn("测量线程:能谱分析开始===" + MParam.IsEDSToRun, true); EDS_Analysis(currHole); log.Info("测量线程:能谱分析结束===" + MParam.IsEDSToRun, true); } } //20.复位 if (MParam.IsBeamShiftResetToRun == true) { log.Info("测量线程:复位开始", true); BeamShiftReset(); } return true; } //初始化拉直操作 public bool Straighten_Handle(CutHole currHole) { //设置拉直的放大倍数 log.Info("测量线程:设置拉直放大倍数,放大倍数为" + MParam.Straighten_Magnification.ToString(), true); if (!m_focuse.SetMagnification(MParam.Straighten_Magnification)) { log.Error("测量线程报错:设置SEM放大倍数为拉直放大倍数失败,放大倍数为" + MParam.Straighten_Magnification.ToString(), false); return false; } //arg.Message = "拉直放大倍数设置成功!"; //arg.State = true; //SendMsg("0-2"); //1、自动对焦 log.Info("测量线程:拉直操作前自动对焦开始", true); var p = new Focusparam(); p.IfAutoBrightnessAndContrast = true; p.mag = MParam.Straighten_Magnification; p.reduceWindowPos = new System.Drawing.Point(402, 128); p.reduceWinWidth = 400; p.reduceWinHeight = 400; p.brightness = MParam.StraightenManualBrightness; p.contrast = MParam.StraightenManualContrast; m_focuse.setFocusParam(p); if (!m_focuse.DoFocusByNewMagnification(MParam.Straighten_Magnification)) { log.Error("测量线程报错:拉直操作自动对焦失败,程序退出。", false); return false; } Thread.Sleep(1000); arg.Message = "拉直操作自动对焦成功!"; arg.State = true; SendMsg("1-0"); //判断是否停止进程 if (key_stop) { log.Info("停止键按下。", true); return false; } //2、拍张照片 String fileName0 = currHole.data_path + "\\" + currHole.HoleName + "_" + MParam.Straighten_Magnification.ToString("0") + "_" + ImageName0; log.Info("测量线程:设置保存图像的是扫描速度保存图像" + fileName0, true); //拍照前改变速度,延时 m_photo.GetParam().Mag = p.mag; m_photo.GetParam().savePath = fileName0; if (!m_photo.TakePhoto()) { log.Error("测量线程报错:拉直拍照失败", false); return false; } arg.Message = "拉直拍照成功!"; arg.Picture_Information.Picture_FullPath = fileName0; arg.State = true; SendMsg("1-1"); //3、华为接口,获取旋转角度 float degree = 0; int direction = 0; int state = 1; ImagePro.Img_OffsetAngle_Direction(fileName0, Convert.ToInt32(MParam.SampleTypeNo()), MParam.Firm, out degree, out direction, out state); log.Info("测量线程:使用图像处理接口:样品" + currHole.HoleNo.ToString() + "初始化拉直角度=" + degree.ToString(), true); log.Info("图像处理结果拉直角度=" + degree.ToString(), true); log.Info("图像处理结果拉直角度=" + degree.ToString(), true); if (state == 0) { log.Info("测量线程:图像处理接口参数state返回为零", false); return false; } if (direction == 2) { degree = -degree; } if (degree != 0) { //4、拉直,旋转R轴 log.Info("测量线程:图像拉直,旋转角度" + degree.ToString(), true); m_locate.RotateStageByDeltaR(degree); //判断是否停止进程 if (key_stop) { log.Info("停止键按下", true); return false; } } arg.Message = "拉直操作完成"; arg.State = true; SendMsg("1-2"); return true; } #region 能谱所有参数 //全局只有一个Extender static ExtenderInterface factoryExtender = ExtenderInterface.Instance; public IExtenderControl iExtender = null; const int ScanFieldSize = 1142; MeasureDB m_MeasDB = null; int m_nXrayId = 0; const String ImageNameEDS = @"EDSImage.tif"; //EDS能谱图片 const String ImageNameEDS4 = @"SEMTrapCPEDS.tif";//传给客户,找到已经切割点 const String ImageNameEDS5 = @"SEMDegreeTrapEDS.tif";//传给客户,水平校正 const String ImageNameEDS6 = @"SEMMagEndEDS.tif";//传给客户,测量层高 /// /// 做能谱分析 /// /// 分析点视场ID,索引号 /// 分析点视场名称 /// 分析点的视场位置ID,图像处理会返回多个Xray分析点,但我们这里只取第一个点所在的视场 /// 成功或失败 public bool DoEDS(CutHole currHole, int a_FieldImagePositionID) { try { string CutHoleName = currHole.HoleName; //XrayID记录 log.Info("程序进入EDS测量!", true); iExtender = factoryExtender.IExtender; Thread.Sleep(500); EDSParam param = MParam.EDSParam; double dDwellTime = param.DwellTime; int nImageType = param.ImageType; double dScanSizes = param.ScanSize; log.Info("dDwellTime=" + dDwellTime.ToString() + " nImageType=" + nImageType.ToString() + " dScanSizes=" + dScanSizes.ToString(), true); iExtender.SetImageAcquistionSetting(dDwellTime, nImageType, dScanSizes); string path = currHole.EDS_path; string edsfn = path + "\\" + a_FieldImagePositionID.ToString() + "_" + ImageNameEDS; log.Info("EDS_PATH=" + edsfn, true); iExtender.GrabImage(edsfn, 0, 0, 0, 0, 0);//get the field eds image where the anylysis point locate. arg.Picture_Information.Picture_FullPath = ""; arg.State = true; SendMsg("4-5");//向界面发送指令:能谱拍照 //arg.State = false; arg.Message = ""; arg.Picture_Information.Picture_FullPath = edsfn; SendMsg("EDSPic"); //送给客户,计算感兴趣的区域 List listPoints = new List(); List LinesStartPoint = new List(); List> Features = new List>(); List lines_height = new List(); System.Drawing.Point area_pt = new System.Drawing.Point(); int width = 1; int height = 1; int AreasNo = 0; long[] XrayData; Dictionary listElement; int xrayCollectMode = param.XrayCollectMode; switch (xrayCollectMode) { case 0://point mode //点数据 int state = 0; ImagePro.EDS_Param_Points(edsfn, a_FieldImagePositionID, Convert.ToInt32(MParam.SampleTypeNo()), MParam.Firm, out listPoints, out state); if (state == 0) { log.Error("点采集失败", false); return false; } log.Info("返回 listPoints=" + listPoints.Count.ToString(), true); if (!m_MeasDB.InsertAnylysisField(currHole.HoleNo, CutHoleName, currHole.EDS_path, listPoints.Count, Features.Count, a_FieldImagePositionID)) { log.Error("插入分析视场失败", false); } iExtender.BeginMultipleAquisition(); Thread.Sleep(100); //点采集 m_nXrayId = 0; foreach (System.Drawing.Point pt in listPoints) { XrayData = new long[2000]; listElement = new Dictionary(); if (iExtender.XrayPointCollecting(1000, pt.X, pt.Y , out XrayData, out listElement)) { string ele = ""; foreach (var s in listElement) { ele += s.ToString(); } log.Info("采集时间=1000", true); log.Info("采集点:(" + pt.X.ToString() + ":" + pt.Y.ToString() + ") 元素:" + ele); //写入数据库 m_nXrayId++; log.Info("点采集结束,写入数据库开始--" + m_nXrayId.ToString(), true); Boolean ret = m_MeasDB.InsertAPointXray(currHole.HoleNo, m_nXrayId, pt.X, pt.Y, XrayData, listElement, a_FieldImagePositionID); Thread.Sleep(1000); } } m_MeasDB.PointDataSubmitted(); iExtender.EndMultipleAquisition(); break; case 1://line mode log.Info("线扫描", true); state = 0; ImagePro.EDS_Param_Lines(edsfn, a_FieldImagePositionID, Convert.ToInt32(MParam.SampleTypeNo()), MParam.Firm, out LinesStartPoint, out lines_height, out state); string sp = ""; foreach (System.Drawing.Point p in LinesStartPoint) { sp += "(" + p.X + "," + p.Y + ")"; } log.Info("线返回数据,lines=" + LinesStartPoint.Count.ToString() + " " + sp+"每条线的长度为:"+ lines_height[0].ToString(), true); if (state == 1) { //将线转换为segment数据 log.Info("将线转换为segment数据", true); for (int i = 0; i < LinesStartPoint.Count; i++) { int wholeHeight = lines_height[i]; int singleHeight = 3;//每5高度形成一个单元 for (int k = 0; k < wholeHeight; k += singleHeight) { List feature = new List(); for (int j = 0; j <= singleHeight; j++) { Segment segment = new Segment(); segment.X = LinesStartPoint[i].X; segment.Y = LinesStartPoint[i].Y+j+k; segment.Length = 1; feature.Add(segment); } Features.Add(feature); } } } else { log.Error("线扫图片处理失败", false); return false; } //线采集 m_nXrayId = 0; iExtender.BeginMultipleAquisition(); //Thread.Sleep(100); log.Info(" Features.Coun的长度为"+ Features.Count.ToString()); for (int i = 0; i < Features.Count; i++) { List listSeg = Features[i]; XrayData = new long[2000]; listElement = new Dictionary(); //if (iExtender.XrayAreaCollectiong(5000, listSeg, out XrayData, out listElement)) if (iExtender.XrayPointCollecting(300, listSeg[0].X, listSeg[0].Y, out XrayData, out listElement)) { string ele = ""; foreach (var s in listElement) { ele += s.ToString(); } log.Info("采集时间=300", true); log.Info("(" + listSeg[0].X + "," + listSeg[0].Y + ") height=3" + " 元素:" + ele); log.Info("线采集结束,写入数据库开始", true); //写入数据库 m_nXrayId++; AreasNo++; //m_MeasDB.InsertAPointXray(currHole.HoleNo, m_nXrayId, listSeg[0].X, listSeg[0].Y, XrayData, listElement, a_FieldImagePositionID); //m_MeasDB.InsertAAreaXay(currHole.HoleNo, m_nXrayId, AreasNo, listSeg, XrayData, listElement, a_FieldImagePositionID); m_MeasDB.SaveToMemory(currHole.HoleNo, m_nXrayId, AreasNo, listSeg, XrayData, listElement, a_FieldImagePositionID); } else { log.Error("线扫xray失败"); } } m_MeasDB.LineDataSubmitted(); iExtender.EndMultipleAquisition(); break; case 2: //area mode //面数据 state = 0; ImagePro.EDS_Param_Areas(edsfn, a_FieldImagePositionID, Convert.ToInt32(MParam.SampleTypeNo()), MParam.Firm, out area_pt, out width, out height, out state); log.Info("面返回数据" + area_pt.X.ToString() + "," + area_pt.Y.ToString() + ",height=" + height.ToString() + ",width=" + width.ToString(), true); if (state == 1) { //将面转换为segment数据,每5行形成一个面 int singleH = 5; for (int k = 0; k < height; k += singleH) { List segments = new List(); for (int j = 0; j <= singleH; j++) { Segment segment = new Segment(); segment.X = area_pt.X; segment.Y = area_pt.Y + j+k; segment.Length = width; segments.Add(segment); } Features.Add(segments); } } else { log.Error("面扫图片处理失败", false); return false; } //面采集 iExtender.BeginMultipleAquisition(); Thread.Sleep(100); foreach (List feature in Features) { XrayData = new long[2000]; listElement = new Dictionary(); log.Info("AreaTime=15000", true); if (iExtender.XrayAreaCollectiong(15000, feature, out XrayData, out listElement)) { string ele = ""; foreach (var s in listElement) { ele += s.ToString(); } log.Info(" 元素:" + ele); log.Info("面采集结束,写入数据库开始", true); //写入数据库 m_nXrayId++; AreasNo++; m_MeasDB.InsertAAreaXay(currHole.HoleNo, m_nXrayId, AreasNo, feature, XrayData, listElement, a_FieldImagePositionID); } else { log.Error("面扫xray失败"); } } iExtender.EndMultipleAquisition(); break; } //存储数据 //向分析点数据库更新 } catch (Exception e) { iExtender.EndMultipleAquisition(); log.Error(e.Message, false); return false; } return true; } /// /// 初始化数据库 /// public void Init_MeasDB() { m_MeasDB = new MeasureDB(m_measureFile); } /// /// 161.自动调整SEM找到切割位置,这里不是简单的拉直旋转 /// /// public bool EDS_MoveToCenter(CutHole currHole) { float x2 = 0, y2 = 0; int state = 0; float tmag = MParam.MoveToCenterMagnification; Focusparam focusPrm = new Focusparam() { IfAutoBrightnessAndContrast = true, mag = tmag, reduceWindowPos = new System.Drawing.Point(256, 192), reduceWinWidth = 400, reduceWinHeight = 400, workingDis = 10,//unit:mm brightness = 50, contrast = 29, tiltCorrAngle = MParam.Correction_Angle_OutSide, ifStig = false, }; m_focuse.setFocusParam(focusPrm); m_focuse.DoFocusBySetMainParameter(); float mag = tmag; String fileName4 = currHole.data_path + "\\EDS\\" + MParam.Firm + "-" + mag.ToString("0") + "-" + currHole.HoleName + "-" + ImageNameEDS4; PhotoParam prm = new PhotoParam() { Mag = mag, savePath = fileName4, }; m_photo.SetPhotoParam(prm); m_photo.TakePhoto(); arg.Picture_Information.Picture_FullPath = fileName4; mArgSend.SendArgumentToScreen("4-2", "", fileName4); if (key_stop) { return false; } //4.将照片传给客户,获取偏移坐标,以及偏移角度 //5.根据坐标控制SEM移动到切孔位置,居中 log.Info("测量线程:图像处理接口,准备移动样品台", true); log.Info("文件名=" + fileName4, true); ImagePro.Img_Trapezoid_Top_Center_Position(fileName4, Convert.ToInt32(MParam.SampleTypeNo()), MParam.Firm, out x2, out y2, out state); log.Info("梯形中心点返回数据=" + x2.ToString() + ", " + y2.ToString(), true); log.Info("准备移动样品台返回状态=" + state.ToString(), true); if (state == 1) { //1. 根据客户PT沉积坐标控制FIB调整到中心位置??????? if (!m_locate.MoveToPixByMoveStage(x2,y2)) { log.Error("测量线程报错:" + arg.Message, false); return false; } } else { log.Error("测量线程报错:" + arg.Message, false); return false; } //判断是否停止进程 if (key_stop) { log.Info("停止键按下", true); return false; } Thread.Sleep(2000); return true; } /// /// 171.能谱第17步找点采集数据 /// /// public bool EDS_ShotSection(CutHole currHole) { int state = 0; Focusparam fprm = new Focusparam() { mag = MParam.Photograph_Magnification, reduceWindowPos = new System.Drawing.Point(402, 128), reduceWinHeight = 600, reduceWinWidth = 600, IfAutoBrightnessAndContrast = true, workingDis = 10,//unit mm brightness = 50, contrast = 29, tiltCorrAngle = MParam.Correction_Angle_Inside, ifStig = false, }; log.Info("自动对焦开始!", true); m_focuse.setFocusParam(fprm); m_focuse.DoFocusBySetMainParameter(); SendMsg("4-4"); fprm.ifStig = false; //add by zjx 2020-12-21 根据客户的需求修改图片的名称 String fileName5 = currHole.data_path + "\\EDS\\" + MParam.Firm + "-" + MParam.Photograph_Magnification.ToString("0") + "-" + currHole.HoleName + "-" + ImageName5; //add by zjx 2020-12-21 根据客户的需求修改图片的名称 end log.Info("fileName5=" + fileName5, true); log.Info("开始拍摄截面照!"); PhotoParam phoPrm = new PhotoParam() { Mag = MParam.Photograph_Magnification, savePath = fileName5, }; //Photo pho = new Photo(phoPrm, iSEM); m_photo.SetPhotoParam(phoPrm); m_photo.TakePhoto(); //判断是否停止进程 if (key_stop) { log.Info("停止键按下", true); return false; } #endregion //if (MParam.IsShotSectionToRun == false)//Is_Photograph 代表 是否是只拍照 //{ #region 获取两个位置上及放大倍数 //计算两个测量区域坐标 List ct = new List(); List mag = new List(); System.Drawing.Point ct0; float magMax; log.Info("图像处理接口,计算两个放大区域坐标,输入图像" + fileName5.ToString(), true); ImagePro.Img_Measure_Region_Position(fileName5, Convert.ToInt32(MParam.SampleTypeNo()), MParam.Firm, out ct, out mag, out ct0, out state); string s = ""; for (int i = 0; i < ct.Count; i++) { s += "输出观测点" + i.ToString() + "为(" + ct[i].X.ToString() + "," + ct[i].Y.ToString() + ")" + "放大倍数=" + mag[i]; } log.Info("测量线程:图像处理接口返回值为" + state.ToString() + ",计算两个放大区域坐标,输入图像" + fileName5.ToString() + "输入样品" + Convert.ToInt32(MParam.SampleTypeNo()) + "输入厂商" + MParam.Firm.ToString() + s + "输出对焦点为(" + ct0.X.ToString() + "," + ct0.Y.ToString() + ")", true); #endregion if (state == 1 && ct.Count != 0 && mag.Count != 0) { Locate recordPostion = new Locate(iSEM); recordPostion.RecordCurrentBeamShiftAndStagePosition(); //找到第一个观测点放大倍数find the first anylysis point and the mag. magMax = mag[0]; #region 移动到梯形左上角 //20201127将视野移动到梯形左上角位置 log.Warn("开始对焦 移动到对焦点( " + ct0.X.ToString() + "," + ct0.Y.ToString() + ")", true); m_locate.MoveToPix(ct0.X,ct0.Y,MParam.PixelSizeCor); Thread.Sleep(200); //add by sun 2020-12-15 拍截面图调用蔡司接口时,要保证扫描速度5以上,自动亮度对比度功能关闭 iSEM.CmdFocusScanSpeed("CMD_SCANRATE5");//CmdFocusRate(5); //add by sun 2020-12-15 拍截面图调用蔡司接口时,要保证扫描速度5以上,自动亮度对比度功能关闭 end cycle_time = iSEM.GetCycleTime(); Thread.Sleep(100 + Convert.ToInt32(cycle_time)); Focusparam fprm1 = new Focusparam() { mag = magMax / 2, reduceWindowPos = new System.Drawing.Point(402, 128), reduceWinHeight = 600, reduceWinWidth = 600, IfAutoBrightnessAndContrast = true, workingDis = 10, brightness = 49.4f, contrast = 30.1f, tiltCorrAngle = MParam.Correction_Angle_Inside, ifStig = false, }; log.Info("自动对焦1开始!", true); m_focuse.setFocusParam(fprm1); m_focuse.DoFocusByNewMagnification(fprm1.mag); log.Info("自动对焦2开始!", true); m_focuse.DoFocusByNewMagnification(magMax); #endregion log.Warn("对焦完成,开始用能谱拍摄观测点照片!并分析图片以获得Xray点位置!"); recordPostion.RestoreLastBeamShiftAndStagePosition(); log.Warn("移动到指定观测点!并准备打Xray"); m_locate.MoveToPix(ct[0].X,ct[0].Y,MParam.PixelSizeCor); SendMsg("4-4");//向界面发送指令:放大并对拍摄点对焦 Thread.Sleep(500); #region 拍照 //log.Info("进入内部观察,不对焦!", true); log.Info("开始EDS拍照,设置放大倍数" + mag[0]); m_focuse.SetMagnification(mag[0]); // m_photo.EnforceContrast(); log.Warn("EDS获取指定观测点图片,并打Xray"); DoEDS(currHole, 1); log.Info("像素扫描:CmdPixelScan"); iSEM.CmdPixelScan(); Thread.Sleep(200); log.Info("改变刷新率 " + "CMD_SCANRATE4"); iSEM.CmdFocusScanSpeed("CMD_SCANRATE4");//CmdFocusRate(4); cycle_time = iSEM.GetCycleTime(); Thread.Sleep(Convert.ToInt32(cycle_time) + 100); SendMsg("4-6");//向界面发生指令:采集能谱信息 } #endregion //} iSEM.SetTiltAngleOff(); Thread.Sleep(200); iSEM.SetScanRotationOff(); Thread.Sleep(200); iSEM.SetBeamShiftX(0); Thread.Sleep(200); iSEM.SetBeamShiftY(0); Thread.Sleep(200); //判断是否停止进程 return true; } /// /// 19.能谱分析 /// /// public bool EDS_Analysis(CutHole currHole) { //1.先降Z轴 37.241左右使工作距离从5mm 变为 10mm,这样方便能谱工作,麻烦是需要从新对焦。由于移动样品台会有误差,容易造成图像偏移,最好从新定位 Locate lo = new Locate(iSEM); lo.MoveZAxisByAbs(MParam.EDSZ / 1000); //判断是否停止进程 if (key_stop) { log.Info("停止键按下", true); return false; } SendMsg("4-0"); //2.角度补偿关闭 // iSEM.SetScanRotationOff(); //在层高分析时,角度补偿已经纠正,能谱分析不必再做 Thread.Sleep(200); iSEM.SetSEMVoltage(MParam.EDSV); Thread.Sleep(200); iSEM.SetSEMIPROBE(MParam.EDSA / 1000000000); Thread.Sleep(200); SendMsg("4-1"); log.Warn("开始梯形移到视野中心!"); if (!EDS_MoveToCenter(currHole)) { return false; } SendMsg("4-3"); //7.调用171步过程 log.Warn("开始打能谱图和打Xray!"); if (!EDS_ShotSection(currHole)) { return false; } return true; } /// /// 20.光束等复位 /// public void BeamShiftReset() { iSEM.SetTiltAngleOff(); Thread.Sleep(200); iSEM.SetScanRotationOff(); Thread.Sleep(200); iSEM.SetBeamShiftX(0); Thread.Sleep(200); iSEM.SetBeamShiftY(0); Thread.Sleep(200); } //add by sun 2020-12-17 不同样品电压、电流不一致,应设置灵活。123456类电压2000V电流200pA,7类5000V 500pA,能谱 7000V 500pA #region 设置电压电流 public bool SetVoltageAndIPROBE() { log.Info("测量线程:设置电压电流 123456类电压2000V电流200pA,7类5000V 500pA,能谱 7000V 500pA 目前是第 " + MParam.SampleType + " 类!", true); bool mRet_VoltageFlag = false; bool mRet_ElecFlag = false; float vot, iprobe; vot = MParam.Voltage; iprobe = MParam.Iprobe; mRet_VoltageFlag = iSEM.SetSEMVoltage(vot); Thread.Sleep(500); mRet_ElecFlag = iSEM.SetSEMIPROBE(iprobe / 1000000000); Thread.Sleep(500); if (!mRet_VoltageFlag) return false; if (!mRet_ElecFlag) return false; log.Info("测量线程:设置电压电流完成!", true); return true; } #endregion //add by sun 2020-12-17 不同样品电压、电流不一致,应设置灵活。123456类电压2000V电流200pA,7类5000V 500pA,能谱 7000V 500pA end //add by sun 2020-12-17 根据客户要求,3大类样品需要在5000倍下在拍摄一张图片,不需要分析 //add by sun 2020-12-17 根据客户要求,3大类样品需要在5000倍下在拍摄一张图片,不需要分析 end //add by zjx 2020-12-21 在测量图上显示放大倍数和像素大小等信息 public void SaveMeasDataPic(String picFullPath, float Mag, float pixelsize) { try { using (System.Drawing.Image img = Image.FromFile(picFullPath)) { Bitmap bmp = new Bitmap(img.Width, img.Height); using (Graphics g = Graphics.FromImage(bmp)) { g.DrawImage(img, 0, 0, img.Width, img.Height); String str = "Mag:" + Mag.ToString("0") + "X\tPixelSize:" + (pixelsize * 1000000).ToString("0.00") + "um"; Font font = new Font("宋体", 16, FontStyle.Bold); SolidBrush sbrush = new SolidBrush(Color.LimeGreen); g.DrawString(str, font, sbrush, new PointF(40, img.Height - 30)); } System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed); Bitmap newBitmap = new Bitmap(img.Width, img.Height, bmpData.Stride, System.Drawing.Imaging.PixelFormat.Format8bppIndexed, bmpData.Scan0); String path = Path.GetDirectoryName(picFullPath) + Path.GetFileNameWithoutExtension(picFullPath) + "_Data" + Path.GetExtension(picFullPath); newBitmap.SetResolution(1, 1); newBitmap.Save(path); } } catch { log.Error("在测量图上显示放大倍数和像素大小信息失败", false); } } //add by zjx 2020-12-21 在测量图上显示放大倍数和像素大小等信息 end } }