Wafer Zero Scan SmartRemote Code

Python

Wafer Zero Scan code used in the Quality Assurance Automation Tool

main 1 file
Wafer Zero Scan …..py
wafer_zero_scan.py 8.1 KB
Wafer Zero Scan …..py 8154 bytes
from src.core.afm import AFM as AFM
from src.core.afm import CHECKER as CHECKER
import src.core.analysis
import src.core.report
import src.core.tiff 
from time import sleep
import numpy as np
import os


class Wafer_Zero_Scan():
    
    def __init__(self):
        self.afm = AFM()
        self.base_dir = ''
        self.sub_dir = 'Wafer Zero Scan'
        self.file_name = 'RAW'
        self.parameter = None
    
    def move_to(self):
        pass


    def set(self):
        if self.parameter['xy_servo_enable'] == 'On':
            self.sub_dir += '/XYservo_On'
            self.parameter['z_scanner_range'] = 70
            self.parameter['xy_scanner_range'] = 100
            self.afm.enable_xy_servo()
       
        elif self.parameter['xy_servo_enable'] == 'Off':
            self.sub_dir += '/XYservo_Off'
            self.log_name = 'Position_XY.csv' 
            self.parameter['z_scanner_range'] = 20
            self.parameter['xy_scanner_range'] = 20
            self.afm.enable_xy_servo('off')
            self.header = 'Repeat, Lable , Input X Pos., Input Y Pos., Output X Pos., Output Y Pos.\n '

        if self.parameter['lift_enable'] == 'On':
            self.sub_dir += '_Lift_On'
        elif self.parameter['lift_enable'] == 'Off':
            self.sub_dir += '_Lift_Off'

        dir_path = self.base_dir + '\\' + self.sub_dir
        self.log_path = dir_path + '\\' + self.log_name
        if not os.path.exists(dir_path):os.makedirs(dir_path)
        fd = open(self.log_path,'w')
        fd.write(self.header)
        fd.close()


        self.afm.set_data_location(self.base_dir, self.sub_dir, self.file_name)
        self.afm.set_head_mode('contact')
        self.afm.clear_channels()
        self.afm.add_channel('ChannelZDriveOrTopography')
        
        self.afm.stop_scan()
        self.afm.set_z_scanner_range(self.parameter['z_scanner_range'])
        self.afm.set_xy_scanner_range(self.parameter['xy_scanner_range'])
        self.bck_scan_geometry = self.afm.get_scan_geometry_all()
        self.bck_scan_option = self.afm.get_scan_option_all()
        self.bck_approach_option = self.afm.get_approach_option_all()
        self.afm.set_scan_geometry(self.parameter['pixels'][0],self.parameter['pixels'][1], 0, 0)
        self.afm.set_scan_option(sine_scan=False,over_scan=False, \
                        over_scan_percent=0, tow_way=False, \
                        det_driven=False, force_slope_correction=False, \
                        interlace=False,slow_scan='off')
        self.afm.set_scan_rate(self.parameter['scan_rate'])
        self.afm.enable_z_servo()
        self.afm.set_z_servo(self.parameter['gain'], self.parameter['setpoint'])
        self.afm.set_approach_option()
    


    def unset(self):    
        self.afm.set_scan_option_dict(self.bck_scan_option)
        self.afm.set_scan_geometry_dict(self.bck_scan_geometry)
        self.afm.set_scan_option_dict(self.bck_scan_option)
    
    def __approach(self):
        self.afm.start_approach('q+s')
        wait_time = self.parameter['post_approach_wait']
        print(f"Waiting for {wait_time} seconds")
        sleep(wait_time)
    
    def __run(self):
        self.afm.trigger_image_scan()
        isscan = True
        while isscan:
            test = self.afm.query_scan_status()
            if test == 'true':isscan=True;
            else:isscan=False
            sleep(2)

    def approach(self):
        pass



    def run(self):
        pos = np.array(self.parameter['target_positions'])  # 'target_positions'에서 좌표 데이터 로드
        
        pos_int = []   # 문자열 배열을 정수형 배열로 변환(빈 문자열은 무시됨)
        for row in pos:
            if row[0] and row[1]:  # 문자열이 비어 있지 않으면
                try:
                    pos_int.append([int(row[0]), int(row[1])])
                except ValueError:
                    print(f"Error converting position to integer: {row}")
                    continue
        
        pos_int = np.array(pos_int)
        norm_speed_x = self.parameter['norm_speed']
        norm_speed_y = self.parameter['norm_speed']

        
        for i, (x, y) in enumerate(pos_int):  # 반복문을 통해 좌표 이동 후 Zero Scan 수행
            self.afm.moveto_xy_stage(x, y, norm_speed_x, norm_speed_y)

            reply = self.afm.query_stage_pos()
            sw_x = reply['x']
            sw_y = reply['y']
            sleep(2)
            
            # 로그 기록
            line = f'{self.iter}, P{i}, {x}, {y}, {sw_x}, {sw_y}\n'
            self.file_name = f'RAW_Repeat_{self.iter}_P{i}'
            self.afm.set_data_location(self.base_dir, self.sub_dir, self.file_name)
            with open(self.log_path, "a") as log_fd:
                log_fd.write(line)


            self.__approach()
            self.__run()
            self.afm.lift_z_stage(1000)



    def done(self):
        self.afm.stop_scan()
        self.afm.stop_scan()
        if self.parameter['lift_enable'] == 'On':
            self.afm.lift_z_stage(1000)

    def stop(self):
        checker = CHECKER()
        checker.abort_approach()
        checker.abort_scan()


    def analyze(self):
        find_dir = self.base_dir + '\\' + self.sub_dir
        tiff_list = src.core.report.ls_tiff(find_dir)
        read_tiff = src.core.tiff.Read_tiff()
        write_tiff = src.core.tiff.Write_tiff()
        
        result_mean = []
        result_min = []
        result_max = []
        result_name = []
        result_rq = []
        for tiff_path in tiff_list:
            tiff_name = tiff_path.split('\\')[-1]
            tmp_name = tiff_name.split('.')[0]
            condition_1 = tmp_name.split('_')[-3] == 'Z Height'
            condition_2 = tmp_name.split('_')[-2] == 'Forward'
            tmp_name_list = tiff_name.split('_')
            tmp_name = ''
            for s in tmp_name_list[1:]: tmp_name += '_' + s
            tmp_name = tmp_name.split('.')[0]
            tmp_name = tmp_name[1:]
            if condition_1 & condition_2:
                dict_tiff , ori_tiff = read_tiff(tiff_path)
                image = dict_tiff['IMAGE']
                flatten_image = src.core.analysis.one_d_flatten(image)
                dict_tiff['IMAGE'] = flatten_image
                cmap = dict_tiff['COLORMAP']
                save_path = self.base_dir + '\\' +self.sub_dir + '\\Flatten\\' + 'Flatten_' + tmp_name + '.tiff'
                report_dir = self.base_dir + '\\' +self.sub_dir + '\\Report\\'
                write_tiff(ori_tiff,dict_tiff,save_path)
                # convert scale : um ->nm
                scale_factor = 1E+3
                tmp_mean, tmp_min, tmp_max = src.core.analysis.cal_statistic(flatten_image)
                tmp_mean *= scale_factor; tmp_min *= scale_factor; tmp_max *= scale_factor
                tmp_rq = src.core.analysis.cal_r_q(flatten_image) * scale_factor
                
                tmp_data = [
                            ['Mean', 'nm',f'{tmp_mean:.3f}'],
                            ['Min', 'nm', f'{tmp_min:.3f}'],
                            ['Max', 'nm', f'{tmp_max:.3f}'],
                            ['Rq', 'nm', f'{tmp_rq:.3f}']
                            ]
                flatten_image *= scale_factor
                src.core.report.default_report(tmp_name, report_dir, dict_tiff, cmap, tmp_data)

                result_name.append(tmp_name)
                result_mean.append(tmp_mean)
                result_min.append(tmp_min)
                result_max.append(tmp_max)
                result_rq.append(tmp_rq)
                
        
        csv_path = self.base_dir + '\\' + self.sub_dir + "\\result.csv"
        result_dict = {'File name': result_name,
                       'Mean (nm)': result_mean,
                       'Min (nm)' : result_min,
                       'Max (nm)' : result_max,
                       'Rq (nm)' :  result_rq
                      } 
        src.core.report.write_csv(result_dict,csv_path,['Mean (nm)','Min (nm)','Max (nm)','Rq (nm)'])
Comments (0)

No comments yet. Be the first to comment!

Snippet Information
Author: taehun.yang
Language: Python
Created: Oct 23, 2025
Updated: 0 minutes ago
Views: 47
Stars: 2

Links & Files

Additional Files (1):
Related Content