using SA_LTT.Base; using SA_LTT.Module.RTC5; using System; using System.Collections.Generic; using System.IO; namespace SA_LTT.Module { public enum ScanMode { JUMP, MARK, } /// /// Scanner 가공 좌표. /// public struct Coord { ScanMode _scanMode; int _x; int _y; public ScanMode ScanMode { get { return _scanMode; } set { _scanMode = value; } } /// /// -8388608 ~ 8388607 /// public int X { get { return _x; } set { if (value > 8388607) { _x = 8388607; } else if (value < -8388608) { _x = -8388608; } else { _x = value; } } } /// /// -8388608 ~ 8388607 /// public int Y { get { return _y; } set { if (value > 8388607) { _y = 8388607; } else if (value < -8388608) { _y = -8388608; } else { _y = value; } } } public Coord(ScanMode scanMode, int x, int y) { _scanMode = ScanMode.JUMP; _x = 0; _y = 0; ScanMode = scanMode; X = x; Y = y; } } public delegate void ScanListAdded(Coord coord); public delegate void ScanListCleared(); public class Scanner : XmlManager { private Equipment _equipment; private object _lock = new object(); public event ScanListAdded ScanListAdded; public event ScanListCleared ScanListCleared; readonly string _fileName = "Scanner.xml"; readonly string _filePath = @"Setting\"; #region Field private string _correctionFilePath; private bool _isInitialized; private bool _isBusy; private uint _isPosition; private int _encoder0; private int _encoder1; private int _encoderCountPerMmX; private int _encoderCountPerMmY; private double _fieldOfView; private uint _halfPeriod; private uint _pulseLength1; private uint _timeBase; private uint _jumpDelay; private uint _markDelay; private uint _polygonDelay; private int _laserOnDelay; private uint _laserOffDelay; private double _xGain; private double _yGain; private bool _isLaserOn; private List scanList = new List(); #endregion #region Property //보정 파일 위치 public string CorrectionFilePath { get { return _correctionFilePath; } set { _correctionFilePath = value; } } /// /// RTC5 Board 마지막 초기화 결과 /// public bool IsInitialized { get { return _isInitialized; } set { _isInitialized = value; } } /// /// RTC5 Board Status. /// public bool IsBusy { get { return _isBusy; } set { _isBusy = value; } } /// /// Scanner에 연결된 Encoder 0 값 /// public int Encoder0 { get { return _encoder0; } set { _encoder0 = value; } } /// /// Scanner에 연결된 Encoder 1 값 /// public int Encoder1 { get { return _encoder1; } set { _encoder1 = value; } } /// /// X축 mm당 Encoder Count /// public int EncoderCountPerMmX { get { return _encoderCountPerMmX; } set { _encoderCountPerMmX = value; } } /// /// Y축 mm당 Encoder Count /// public int EncoderCountPerMmY { get { return _encoderCountPerMmY; } set { _encoderCountPerMmY = value; } } /// /// 1mm당 bits 수. /// public double BitsPerMm { get { if (FOV == 0) { return 0; } else { return Math.Pow(2, 20 - 1) / (FOV / 2); } } } /// /// Scanner의 field of view /// public double FOV { get { return _fieldOfView; } set { _fieldOfView = value; } } /// /// Scanner Kfactor : FOV에 따라 정해짐 /// public double KFactor { get { if (FOV == 0) { return 1.0; } else { return Math.Pow(2, 20) / (FOV / 2); } } } /// /// Half of the laser signal period /// 1bit equals 1/64 us. Allowed range [0 ~ 2^(32 -1)] /// public uint HalfPeriod { get { return _halfPeriod; } set { if (value > 2147483648) { _halfPeriod = 2147483648; } else { _halfPeriod = value; } } } /// /// Pulse widths of signals Laser1 /// 1bit equals 1/64 us. Allowed range [0 ~ 2^(32 -1)] /// public uint PulseLength1 { get { return _pulseLength1; } set { if (value > 2147483648) { _pulseLength1 = 2147483648; } else { _pulseLength1 = value; } } } /// /// Pulse widths of signals Laser2 /// The value of PulseLength2 is ignored and is no longer used /// public uint PulseLength2 { get { return _pulseLength1; } } /// /// time base; 0 corresponds to 1 microsecond /// In RTC5 mode, the value is ignored and the clock frequency is fixed /// at 64 MHz. 1 bit equals 1/64 µs /// public uint TimeBase { get { return _timeBase; } set { _timeBase = value; } } /// /// Jump delay in 10 microseconds /// public uint JumpDelay { get { return _jumpDelay; } set { _jumpDelay = value; } } /// /// Mark delay in 10 microseconds /// public uint MarkDelay { get { return _markDelay; } set { _markDelay = value; } } /// /// Polygon delay in 10 microseconds /// public uint PolygonDelay { get { return _polygonDelay; } set { _polygonDelay = value; } } /// /// Laser on delay in microseconds /// public int LaserOnDelay { get { return _laserOnDelay; } set { _laserOnDelay = value; } } /// /// Laser off delay in microseconds /// public uint LaserOffDelay { get { return _laserOffDelay; } set { _laserOffDelay = value; } } public uint IsPosition { get { return _isPosition; } set { _isPosition = value; } } public double XGain { get { return _xGain; } set { if (value < 0) _xGain = 1; _xGain = value; } } public double YGain { get { return _yGain; } set { if (value < 0) _yGain = 1; _yGain = value; } } public bool IsLaserOn { get { return _isLaserOn; } set { _isLaserOn = value; } } #endregion public Scanner(Equipment equipment) { _equipment = equipment; TimeBase = 0; Refresh(); } public Scanner() { } public void Refresh() { DirectoryInfo directoryInfo = new DirectoryInfo(Equipment.settingFilePath + _filePath); if (directoryInfo.Exists == false) { directoryInfo.Create(); } FileInfo fileInfo = new FileInfo(directoryInfo.FullName + _fileName); if (fileInfo.Exists == false) { fileInfo.Create().Close(); SaveFile(fileInfo.FullName, this); } else { Scanner scanner = new Scanner(); scanner = ReadFile(fileInfo.FullName); Copy(scanner); } } /// /// Error 발생했는지 확인. /// /// private bool ErrorExist() { uint errorCode = RTC5Wrap.get_error(); if (errorCode == 0) return false; byte[] errorBytes = BitConverter.GetBytes(errorCode); System.Collections.BitArray errorBits = new System.Collections.BitArray(errorBytes); string str = string.Empty; if (errorBits[0] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0800_RTC5_NO_BOARD_FOUND); if (errorBits[1] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0801_RTC5_ACCESS_DENIED); if (errorBits[2] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0802_RTC5_COMMAND_NOT_FORWARDED); if (errorBits[3] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0803_RTC5_NO_RESPONSE_FROM_BOARD); if (errorBits[4] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0804_RTC5_INVALID_PARAMETER); if (errorBits[5] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0805_RTC5_LIST_PROCESSING_IS_NOT_ACTIVE); if (errorBits[6] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0806_RTC5_LIST_COMMAND_REJECTED); if (errorBits[7] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0807_RTC5_LIST_COMMAND_HAS_BEEN_COVERTED); if (errorBits[8] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0808_RTC5_VERSION_ERROR_RTC5_DLL_VERSION); if (errorBits[9] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0809_RTC5_VERIFY_ERROR_RTC5_DLL_VERSION); if (errorBits[10] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0810_RTC5_DSP_VERSION_ERROR); if (errorBits[11] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0811_RTC5_DLL_INTERNAL_WINDOWS_MEMORY_REQUEST_FAIL); if (errorBits[12] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0812_RTC5_EEPROM_READ_OR_WRITE_ERROR); if (errorBits[16] == true) _equipment.alarmManager.Occur(AlarmCode.AL_0813_RTC5_ERROR_READING_PCI_CONFIGURATION_REGISTER); RTC5Wrap.reset_error(errorCode); return true; } /// /// Load Program 후 반환된 코드로 Error 발생 확인. /// /// /// private bool LoadProgramFileErrorExist(uint errorCode) { switch (errorCode) { case 0: return false; case 1: _equipment.alarmManager.Occur(AlarmCode.AL_0814_RTC5_RESET_ERROR); return true; case 2: _equipment.alarmManager.Occur(AlarmCode.AL_0815_RTC5_UNRESET_ERROR); return true; case 3: _equipment.alarmManager.Occur(AlarmCode.AL_0816_RTC5_FILE_ERROR); return true; case 4: _equipment.alarmManager.Occur(AlarmCode.AL_0817_RTC5_FORMAT_ERROR); return true; case 5: _equipment.alarmManager.Occur(AlarmCode.AL_0818_RTC5_SYSTEM_ERROR); return true; case 6: _equipment.alarmManager.Occur(AlarmCode.AL_0819_RTC5_ACCESS_ERROR); return true; case 7: _equipment.alarmManager.Occur(AlarmCode.AL_0820_RTC5_VERSION_ERROR); return true; case 8: _equipment.alarmManager.Occur(AlarmCode.AL_0821_RTC5_SYSTEM_DRIVER_NOT_FOUND); return true; case 9: _equipment.alarmManager.Occur(AlarmCode.AL_0822_RTC5_DRIVER_CALL_ERROR); return true; case 10: _equipment.alarmManager.Occur(AlarmCode.AL_0823_RTC5_CONFIGURATION_ERROR); return true; case 11: _equipment.alarmManager.Occur(AlarmCode.AL_0824_RTC5_FPGA_FIRMWARE_ERROR); return true; case 12: _equipment.alarmManager.Occur(AlarmCode.AL_0825_RTC5_PCI_DOWNLOAD_ERROR); return true; case 13: _equipment.alarmManager.Occur(AlarmCode.AL_0826_RTC5_BUSY_ERROR); return true; case 14: _equipment.alarmManager.Occur(AlarmCode.AL_0827_RTC5_DSP_MEMORY_ERROR); return true; case 15: _equipment.alarmManager.Occur(AlarmCode.AL_0828_RTC5_VERIFY_ERROR); return true; case 16: _equipment.alarmManager.Occur(AlarmCode.AL_0829_RTC5_PCI_ERROR); return true; default: return true; } } /// /// 보정파일 읽어올때 반환된 코드로 Error 발생 확인. /// /// /// private bool LoadCorrectionFileErrorExist(uint errorCode) { switch (errorCode) { case 0: return false; case 1: _equipment.alarmManager.Occur(AlarmCode.AL_0830_RTC5_FILE_ERROR); return true; case 2: _equipment.alarmManager.Occur(AlarmCode.AL_0831_RTC5_MEMORY_ERROR); return true; case 3: _equipment.alarmManager.Occur(AlarmCode.AL_0832_RTC5_FILE_OPEN_ERROR); return true; case 4: _equipment.alarmManager.Occur(AlarmCode.AL_0833_RTC5_DSP_MEMORY_ERROR); return true; case 5: _equipment.alarmManager.Occur(AlarmCode.AL_0834_RTC5_PCI_DOWNLOAD_ERROR); return true; case 8: _equipment.alarmManager.Occur(AlarmCode.AL_0835_RTC5_SYSTEM_DRIVER_NOT_FOUND); return true; case 10: _equipment.alarmManager.Occur(AlarmCode.AL_0836_RTC5_PARAMETER_ERROR); return true; case 11: _equipment.alarmManager.Occur(AlarmCode.AL_0837_RTC5_ACCESS_ERROR); return true; case 12: _equipment.alarmManager.Occur(AlarmCode.AL_0838_RTC5_WARNING_3D_CORRECTION_TABLE); return true; case 13: _equipment.alarmManager.Occur(AlarmCode.AL_0839_RTC5_BUSY_ERROR); return true; case 14: _equipment.alarmManager.Occur(AlarmCode.AL_0840_RTC5_PCI_UPLOAD_ERROR); return true; case 15: _equipment.alarmManager.Occur(AlarmCode.AL_0841_RTC5_VERIFY_ERROR); return true; default: return true; } } /// /// RTC 5 DLL 초기화. /// /// private bool InitializeRTC5Dll() { // ====== RTC 5 Dll 초기화 ====== RTC5Wrap.init_rtc5_dll(); if (ErrorExist()) { return false; } else { return true; } } /// /// RTC5 RBF, OUT, DAT 파일 로드 /// /// private bool LoadProgramFiles() { uint errorCode = RTC5Wrap.load_program_file(null); if (LoadProgramFileErrorExist(errorCode)) { return false; } else { return true; } } /// /// 보정파일 로드 /// /// 보정파일 위치 /// private bool LoadCorrectionFiles(string filePath) { uint errorCode = RTC5Wrap.load_correction_file(CorrectionFilePath, 1, // table; #1 is used by default 2); // 2D correction files and 3D correction files are sotred as a 2D correction table. if (LoadCorrectionFileErrorExist(errorCode)) { return false; } else { return true; } } /// /// RTC5 보드 초기화. [기본 위치 : 실행파일 위치] /// /// 보정 파일 위치 /// public bool Initialize(string correctionFilePath = null) { lock (_lock) { if (InitializeRTC5Dll() == false) { IsInitialized = false; return false; } if (LoadProgramFiles() == false) { IsInitialized = false; return false; } if (LoadCorrectionFiles(correctionFilePath) == false) { IsInitialized = false; return false; } } RTC5Wrap.home_position(0, 0); if(SetLaserDelays() && SetLaserTiming() && SetScannerDelay()) { IsInitialized = true; } else { IsInitialized = false; } RTC5Wrap.set_laser_control(1); return true; } /// /// List 동작 중 확인. /// /// public bool GetBusy() { try { if (IsInitialized) { lock (_lock) { uint ubusy, position; RTC5Wrap.get_status(out ubusy, out position); IsBusy = ubusy != 0; IsPosition = position; } } else { IsBusy = false; } return IsBusy; } catch(Exception e) { EquipmentLogManager.Instance.WriteExceptionLog(e.StackTrace); return false; } } public bool GetLaserOn() { try { if (IsInitialized) { lock (_lock) { IsLaserOn = RTC5Wrap.get_value((ushort)0) == (short)1; } } else { IsLaserOn = false; } return IsLaserOn; } catch(Exception e) { EquipmentLogManager.Instance.WriteExceptionLog(e.StackTrace); return false; } } /// /// Laser timing 설정 [Laser Half of signal period, Laser Pulse widths, Time Base] /// /// public bool SetLaserTiming() { RTC5Wrap.set_start_list(1); RTC5Wrap.set_laser_timing(HalfPeriod, PulseLength1, PulseLength2, TimeBase); RTC5Wrap.set_end_of_list(); RTC5Wrap.execute_list(1); //CtlFrequency(1000, 0.085f); if (ErrorExist()) { return false; } else { return true; } } /// /// Scanner delay 설정. [jump delay, mark delay, polygon delay] /// /// public bool SetScannerDelay() { RTC5Wrap.set_start_list(1); RTC5Wrap.set_scanner_delays(JumpDelay, MarkDelay, PolygonDelay); RTC5Wrap.set_end_of_list(); RTC5Wrap.execute_list(1); if (ErrorExist()) { return false; } else { return true; } } /// 주파수와 펄스폭 설정 /// 주파수 (Hz) /// 펄스폭 (usec) /// public virtual bool CtlFrequency(float frequency, float pulseWidth) { if (IsBusy) { return false; } if ((double)frequency <= 0.0 || (double)pulseWidth <= 0.0) { return false; } double num1 = Math.Round(1.0 / (double)frequency * 1000000.0 / 2.0, 3); double num2 = Math.Round((double)pulseWidth, 3); RTC5Wrap.set_start_list(1); RTC5Wrap.set_laser_timing((uint)Math.Floor(num1 * 64.0), (uint)Math.Floor(num2 * 64.0), (uint)Math.Floor(num2 * 64.0), 0U); RTC5Wrap.set_end_of_list(); RTC5Wrap.execute_list(1); return true; } /// /// Laser delay 설정 [Laser on delay, Laser off delay] /// /// public bool SetLaserDelays() { RTC5Wrap.set_start_list(1); RTC5Wrap.set_laser_delays(LaserOnDelay, LaserOffDelay); RTC5Wrap.set_end_of_list(); RTC5Wrap.execute_list(1); if (ErrorExist()) { return false; } else { return true; } } public void LaserOn() { RTC5Wrap.laser_signal_on(); } public void LaserOff() { RTC5Wrap.laser_signal_off(); } /// /// Set jump speed, mark speed /// /// mm/s /// mm/s /// public bool SetJumpMarkSpeed(double jumpSpeed, double markSpeed) { if (jumpSpeed == 0) jumpSpeed = 10; if (markSpeed == 0) markSpeed = 10; double jump = (BitsPerMm / 1000) * jumpSpeed; double mark = (BitsPerMm / 1000) * markSpeed; RTC5Wrap.set_start_list(1); RTC5Wrap.set_jump_speed(jump); RTC5Wrap.set_mark_speed(mark); RTC5Wrap.set_end_of_list(); RTC5Wrap.execute_list(1); if (ErrorExist()) { return false; } else { return true; } } /// /// RCT5 보드에 연결되어 있는 Encoder 값 읽기. /// /// public bool GetEncoder() { int encoder0, encoder1; RTC5Wrap.get_encoder(out encoder0, out encoder1); Encoder0 = encoder0; Encoder1 = encoder1; if (ErrorExist()) { return false; } else { return true; } } /// /// Scan list Data 반환 /// /// public Coord[] GetScanList() { return scanList.ToArray(); } /// /// Scan list 초기화 /// /// public bool ClearScanList() { scanList.Clear(); ScanListCleared.Invoke(); return true; } /// /// Scan list에 가공 정보 추가 /// /// /// public bool AddScanList(Coord coord) { scanList.Add(coord); ScanListAdded?.Invoke(coord); return true; } /// /// /// /// /// mm /// mm /// public bool AddScanList(ScanMode scanMode, double x, double y) { Coord coord = new Coord(); coord.ScanMode = scanMode; coord.X = (int)Math.Floor(x * BitsPerMm); coord.Y = (int)Math.Floor(y * BitsPerMm); scanList.Add(coord); ScanListAdded?.Invoke(coord); return true; } /// /// 가공 시작 /// /// public bool ExcuteScanList(bool onTheFly = false) { RTC5Wrap.set_start_list(1); if (onTheFly) { double ScaleX; double ScaleY; if (EncoderCountPerMmX == 0) { ScaleX = 0; } else { ScaleX = BitsPerMm / EncoderCountPerMmX; RTC5Wrap.set_fly_x(ScaleX); } if (EncoderCountPerMmY == 0) { ScaleY = 0; } else { ScaleY = BitsPerMm / EncoderCountPerMmY; RTC5Wrap.set_fly_y(ScaleY); } RTC5Wrap.set_rot_center(0, 0); } foreach (Coord coord in scanList) { int x = (int)(coord.X * XGain); int y = (int)(coord.Y * YGain); if (coord.ScanMode == ScanMode.JUMP) { //RTC5Wrap.jump_abs(coord.X, coord.Y); RTC5Wrap.jump_abs(x, y); } else { //RTC5Wrap.mark_abs(coord.X, coord.Y); RTC5Wrap.mark_abs(x, y); } } if (onTheFly) { RTC5Wrap.fly_return(0, 0); } else { RTC5Wrap.jump_abs(0, 0); } RTC5Wrap.set_end_of_list(); RTC5Wrap.execute_list(1); if (ErrorExist()) { return false; } else { return true; } } public bool Stop() { RTC5Wrap.stop_execution(); if (ErrorExist()) { return false; } else { return true; } } public bool Save() { DirectoryInfo directoryInfo = new DirectoryInfo(Equipment.settingFilePath + _filePath); if (directoryInfo.Exists == false) { directoryInfo.Create(); } FileInfo fileInfo = new FileInfo(directoryInfo.FullName + _fileName); if (fileInfo.Exists == false) { fileInfo.Create().Close(); } SetScannerDelay(); SetLaserDelays(); SetLaserTiming(); return TrySaveFile(fileInfo.FullName, this); } public void AddScanField(int linecount = 5, double fieldSize = 15) { double intervalLength = 0; if (linecount % 2 == 0) // Line count가 짝수 일 때 { intervalLength = fieldSize / linecount; //X축 그리기. for (int i = linecount / 2; i > 0; i--) { AddScanList(ScanMode.JUMP, -(fieldSize / 2 + 1), intervalLength * i); AddScanList(ScanMode.MARK, (fieldSize / 2 + 1), intervalLength * i); } for (int i = -(linecount / 2); i < 0; i++) { AddScanList(ScanMode.JUMP, -(fieldSize / 2 + 1), intervalLength * i); AddScanList(ScanMode.MARK, (fieldSize / 2 + 1), intervalLength * i); } //Y축 그리기 for (int i = linecount / 2; i > 0; i--) { AddScanList(ScanMode.JUMP, intervalLength * i, -(fieldSize / 2 + 1)); AddScanList(ScanMode.MARK, intervalLength * i, (fieldSize / 2 + 1)); } for (int i = -(linecount / 2); i < 0; i++) { AddScanList(ScanMode.JUMP, intervalLength * i, -(fieldSize / 2 + 1)); AddScanList(ScanMode.MARK, intervalLength * i, (fieldSize / 2 + 1)); } } else // Line count가 홀수 일 때 { intervalLength = fieldSize / (linecount - 1); //X축 그리기. for (int i = -(linecount - 1) / 2; i <= (linecount - 1) / 2; i++) { AddScanList(ScanMode.JUMP, -(fieldSize / 2 + 1), intervalLength * i); AddScanList(ScanMode.MARK, (fieldSize / 2 + 1), intervalLength * i); } //Y축 그리기 for (int i = -(linecount - 1) / 2; i <= (linecount - 1) / 2; i++) { AddScanList(ScanMode.JUMP, intervalLength * i, -(fieldSize / 2 + 1)); AddScanList(ScanMode.MARK, intervalLength * i, (fieldSize / 2 + 1)); } } } } }