using Aurora.Api; using AuroraLaserScanner; using Hermes; using MilUI; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Reactive.Disposables; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using VisionCommon; using WeldingCommon.Alarm; using WeldingCommon.Data; using WeldingCommon.Settings; namespace WeldingCommon.Sequences { class SeqScannerCalibration { static readonly Logger Log = new Logger("SeqScannerCalibration"); const string SEQ_ENTER = "[SeqScannerCalibration] SEQ_ENTER"; // inputParam 으로 받자. const string SEQ_CHECK_INIT_CONDITION = "[SeqScannerCalibration] Check Init Condition"; // Check Init Condition const string SEQ_CAPTURE_NEXT_POSITION = "[SeqScannerCalibration] Capture Next Position"; const string SEQ_FIND_PATTERN = "[SeqScannerCalibration] Find Pattern"; const string SEQ_DECISION_FIND_RESULT = "[SeqScannerCalibration] Decision Find Result"; const string SEQ_DECISION_CHECK_OFFSET = "[SeqScannerCalibration] Decision Check Offset"; const string SEQ_SET_VISION_POSITION = "[SeqScannerCalibration] Set Vision Position"; const string SEQ_SET_VISION_POSITION_FAILED = "[SeqScannerCalibration] Set Vision Position Failed"; const string SEQ_DECISION_CHECK_NEXT_POSITION_EXISTS = "[SeqScannerCalibration] Decision Check Next Postion Exists"; const string SEQ_SAVE_RESULT_DATA = "[SeqScannerCalibration] Save Result Data"; const string SEQ_WRITE_COMPLETION_PLC_BIT = "[SeqScannerCalibration] Write Completion PLC Bit"; const string SEQ_COMPLETE = "[SeqScannerCalibration] SEQ_COMPLETE"; // 완료 static class ContextKey { public const string KEY_SEQUENCE_RESULT = "KEY_SEQUENCE_RESULT_SeqScannerCalibration"; public const string KEY_SEQUENCE_START_TIME = "KEY_SEQUENCE_START_TIME_SeqScannerCalibration"; } private SeqScannerCalibration_InitParam _InitParam; private Scanner _Scanner; private SystemParameter _SystemParameter; private FieldScanParams _FieldScanParams; private PatternResult _PatternResult; private PatternAgentBase _PatternAgent; private ProcPatternInfo _ProcPatternInfo; private PatternInfo _PatternInfo; private ICameraDisplay _CameraDisplay; private CameraAgentBase _CameraAgent; //private eListNo _MarkingGridScannerListNo; //private double _JumpSpeed; private List _MarkingPositionList_MM; // MM 은 밀리미터 단위 인듯 private int _PositionIndex = 0; private double _CurrentPositionX = 0; private double _CurrentPositionY = 0; private double _PatternOffsetX = 0; private double _PatternOffsetY = 0; private int _CheckOffsetCount = 0; private int _FindPatternCount = 0; private double _Score = 0; private object _GrabImage; private int _JumpSleepTime = 0; private int _Is1stepCount; private int _NotFoundCount; public SequenceBuilder Builder() { var sb = new SequenceBuilder(1000, true); ////var sb = new SequenceBuilder(1000, false); // With log sb.AddAction(SEQ_ENTER, Sequence_Enter); sb.AddDecision(SEQ_CHECK_INIT_CONDITION, CheckInitCondition); //분기 sb.AddAction(SEQ_CAPTURE_NEXT_POSITION, CaptureNextPosition); sb.AddAction(SEQ_FIND_PATTERN, FindPattern); sb.AddDecision(SEQ_DECISION_FIND_RESULT, DecisionFindResult); sb.AddDecision(SEQ_DECISION_CHECK_OFFSET, DecisionCheckOffset); sb.AddAction(SEQ_SET_VISION_POSITION_FAILED, SetVisionPositionFailed); sb.AddAction(SEQ_SET_VISION_POSITION, SetVisionPosition); sb.AddDecision(SEQ_DECISION_CHECK_NEXT_POSITION_EXISTS, DecisionCheckNextPostionExists); sb.AddAction(SEQ_SAVE_RESULT_DATA, SaveResultData); // 해당 공정의 정보를 기록 함. sb.AddAction(SEQ_WRITE_COMPLETION_PLC_BIT, WriteCompletionPLCBit); // 정상적으로 종료 되면, complete bit write sb.AddAction(SEQ_COMPLETE, Complete); sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_CAPTURE_NEXT_POSITION] > sb[SEQ_FIND_PATTERN] > sb[SEQ_DECISION_FIND_RESULT] > sb[SEQ_DECISION_CHECK_OFFSET] > sb[SEQ_SET_VISION_POSITION] > sb[SEQ_DECISION_CHECK_NEXT_POSITION_EXISTS] > sb[SEQ_SAVE_RESULT_DATA] > sb[SEQ_WRITE_COMPLETION_PLC_BIT] > sb[SEQ_COMPLETE]); sb.Flows(sb[SEQ_DECISION_FIND_RESULT] > sb[SEQ_SET_VISION_POSITION_FAILED] > sb[SEQ_DECISION_CHECK_NEXT_POSITION_EXISTS]); sb.Flows(sb[SEQ_DECISION_FIND_RESULT] > sb[SEQ_FIND_PATTERN]); sb.Flows(sb[SEQ_DECISION_CHECK_OFFSET] > sb[SEQ_SET_VISION_POSITION_FAILED] > sb[SEQ_DECISION_CHECK_NEXT_POSITION_EXISTS]); sb.Flows(sb[SEQ_DECISION_CHECK_OFFSET] > sb[SEQ_FIND_PATTERN]); sb.Flows(sb[SEQ_DECISION_CHECK_NEXT_POSITION_EXISTS] > sb[SEQ_CAPTURE_NEXT_POSITION]); sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_COMPLETE] > sb.Final); sb.OnStopped(OnStopped); sb.OnForceError(OnForceError); //sb.Draw("SeqScannerCalibration.pdf"); return sb; } private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param) { Log.I("ScannerCalibration Start"); _InitParam = param.Param as SeqScannerCalibration_InitParam; _SystemParameter = context.GetSetting(CommonSettingIds.SystemParameter); InitDev(context); if (!CheckDeviceReady(context)) { //Check Device AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_ScannerCalibration_DeviceNotReady); Log.E("The devices are not active."); return null; } if (InitSequenceVariable(context) == false) { AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_ScannerCalibration_InitVariableError); return null; } return SequenceAction.Ok(); } private bool InitSequenceVariable(SequenceContext context) { _FieldScanParams = FieldScanParams.GetInstance(); if (_InitParam.Is1StepCalibration) { _PositionIndex = _InitParam.SelectedRowIndex; } else { _PositionIndex = 0; } _FindPatternCount = 0; _JumpSleepTime = 1000; _Is1stepCount = 0; _NotFoundCount = 0; // Initialize MasterPos _MarkingPositionList_MM = new List(); float tempX = 0, tempY = 0; float posX = 0; float posY = 0; for (int column = 0; column < _InitParam.ColumnCount; column++) { tempX = _InitParam.UnitLength * ((column % _InitParam.ColumnCount) - (_InitParam.ColumnCount - 1) / 2f); for (int row = 0; row < _InitParam.RowCount; row++) { tempY = _InitParam.UnitLength * ((row % _InitParam.RowCount) - (_InitParam.RowCount - 1) / 2f); posX = tempX * (float)Math.Cos(_InitParam.TiltingDegree * Math.PI / 180) - tempY * (float)Math.Sin(_InitParam.TiltingDegree * Math.PI / 180); posY = tempX * (float)Math.Sin(_InitParam.TiltingDegree * Math.PI / 180) + tempY * (float)Math.Cos(_InitParam.TiltingDegree * Math.PI / 180); _MarkingPositionList_MM.Add(new PointF(posX, posY)); } } // InitializeCameraDevice //FovInfo는 UI 생성자를 통해 전달된 Params의 FovInfo를 사용한다. _PatternAgent = PatternAgentManager.GetAgent(_InitParam.FovInfo); //VisionRecipe - FovRecipes - PatternRecipe에 ProcPatternInfos를 수동으로 추가줘야 Pattern Apply시 ProcPatternInfo가 생성됨 //ProcPatternInfos는 Recipe에 1개만 등록해두고 필요 시 패턴의 apply를 변경하도록 하자 _ProcPatternInfo = VisionRecipeManager.GetProcPatternInfo(_InitParam.FovInfo, "Align1", eProcPatternId.ProcPattern1); //ProcPatternInfo로 등록한 PatternInfo(패턴 정보)를 받아오자 _PatternInfo = _PatternAgent.GetPatternInfo(_InitParam.FovInfo, _ProcPatternInfo.PatternId); // 패턴을 찾는 공정이 있으므로 eDisplayType을 반드시 Pattern으로 해야 한다. _CameraDisplay = CameraDisplayManager.GetDisplay(_InitParam.FovInfo, eDisplayType.Pattern); //카메라 에이전트 가져오기 _CameraAgent = CameraAgentManager.GetCameraAgent(_InitParam.FovInfo); // InitGrabImageBuffer try { //GrabImageBuffer를 Update할 필요가 있으면 아래 코드 사용 //IpAdapter.Instance.IpEnvironment.FreeImage(_GrabImage); _GrabImage = _CameraAgent.CloneImageBuffer(); } catch (Exception e) { Log.E(e.Message); } // ClearCameraDisplay ClearCameraDisplay(); return true; } public void ClearCameraDisplay() { if (_CameraAgent != null) { _CameraDisplay.ClearAllGraphics(); } } private void InitDev(SequenceContext context) { //Device Init _Scanner = context.GetLaserScanner(); } private bool CheckDeviceReady(SequenceContext context) { List deviceListToCheck = new List() { CommonDeviceIds.Scanner }; bool isAllDeviceActivated = true; foreach (string item in deviceListToCheck) { if (!context.Devices[item].IsActivated) { isAllDeviceActivated = isAllDeviceActivated && false; } } return isAllDeviceActivated; } private bool CheckCondition() { if (CheckCondition_Interlock()) { if (InitializeDeviceStatus()) { return true; } } return false; } private bool CheckCondition_Interlock() { // PLC, Data, IO 등 Interlock checking return true; } private bool InitializeDeviceStatus() { // Device 의 status 를 맞추자. ScannerCalibration 는 Scanner 를 설정 한다. 초기 설정 해야 하는 값으로 _Scanner.SetJumpSpeed(_SystemParameter.FieldScanJumpSpeed); return true; } private string CheckInitCondition(SequenceContext context, string id, ICollection branches, SequenceActionResult param) { if (!CheckCondition()) { AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_ScannerCalibration_CheckInitCondition); Log.E("Condition is not in state to start sequence."); context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, false); return SEQ_COMPLETE; } var startTime = DateTime.Now; context.PutValue(ContextKey.KEY_SEQUENCE_START_TIME, startTime); Log.D("DoCrossLaserShot Sequence CheckInitCondition"); return SEQ_CAPTURE_NEXT_POSITION; } private SequenceActionResult CaptureNextPosition(SequenceContext context, string id, SequenceActionResult param) { _CurrentPositionX = _MarkingPositionList_MM[_PositionIndex].X + _InitParam.MasterPosOffsetX; _CurrentPositionY = _MarkingPositionList_MM[_PositionIndex].Y + _InitParam.MasterPosOffsetY; //1.스캐너 필드 위치 이동(Jump이동) _Scanner.JumpAbs(_CurrentPositionX, _CurrentPositionY); Thread.Sleep(_JumpSleepTime); // 스캐너 점프 이동 후 안정화 시간 추가 Log.I(string.Format($"Move Next Position [{_CurrentPositionX},{_CurrentPositionY}]")); _CheckOffsetCount = 0; return SequenceAction.Ok(); } private SequenceActionResult FindPattern(SequenceContext context, string id, SequenceActionResult param) { try { if(_Is1stepCount > 5) { Log.I(string.Format($"Pattern Not Found. 1StepCount OverFlow")); return null; } _GrabImage = DoGrab(); _PatternResult = _PatternAgent.Find(_PatternInfo, _GrabImage, _CameraDisplay); } catch (Exception e) { Log.E(e.Message); } return SequenceAction.Ok(); } public object DoGrab() { var image = _GrabImage; _CameraAgent.GrabSync(_CameraDisplay, ref image); _GrabImage = image; return _GrabImage; } private string DecisionFindResult(SequenceContext context, string id, ICollection branches, SequenceActionResult param) { double offsetX = 0; double offsetY = 0; if (_PatternResult.Status == eFoundStatus.FoundOK) { Log.I($"PatternResult : {_PatternResult}"); _PatternAgent.Draw(_PatternInfo, _PatternResult, _CameraDisplay); //patternResult의 X,Y 좌표값을 받아서 Image의 센터와의 차이만큼 계산하여 스캐너 구동 후 다시 매칭 var patternRsultInfo = _PatternResult.MoreInfo as FixtureResult; offsetX = patternRsultInfo.Origin.X; offsetY = patternRsultInfo.Origin.Y; TranslateImageToField(offsetX, offsetY, out _PatternOffsetX, out _PatternOffsetY); Log.I($"PatternOffsetX : {_PatternOffsetX}, PatternOffsetY : {_PatternOffsetY}"); //스캐너 구동 후 다시 한번 FindPattern후 DecisioCheckOffset으로 넘어감 if (_FindPatternCount < 1) { _FindPatternCount++; //_Scanner.JumpRel(patternOffsetX, patternOffsetY); _Scanner.JumpRel(_PatternOffsetX, _PatternOffsetY); Log.I(string.Format($"Move Rel Position [{_PatternOffsetX},{_PatternOffsetY}")); Thread.Sleep(_JumpSleepTime); //스캐너 점프 이동 후 안정화 시간 추가 ClearCameraDisplay(); return SEQ_FIND_PATTERN; } else { _FindPatternCount = 0; return SEQ_DECISION_CHECK_OFFSET; } } if (_InitParam.Is1StepCalibration) { _Is1stepCount++; Log.I(string.Format($"Is1stepCount : {_Is1stepCount} ")); return SEQ_FIND_PATTERN; } if (_NotFoundCount > 5) { Log.I(string.Format($"Pattern Not Found. NotFoundCount OverFlow. Next Sequnece : SetVisionPositionFailed.")); return SEQ_SET_VISION_POSITION_FAILED; } else { _NotFoundCount++; Log.I(string.Format($"NotFoundCount : {_NotFoundCount} ")); return SEQ_FIND_PATTERN; } } private void TranslateImageToField(double imgPosX, double imgPosY, out double realPosX, out double realPosY) { //여기 수정 필요.. 하드 코딩 되어있으면 안되겠네.. var camConfig = VisionEnv.AuroraEnv.DeviceManager.GetDeviceConfig(eCamId.CAM1.ToString()) as CamConfig; //var imageWidthChild = camConfig.ImageSize.Width / 2; //var imageHeightChild = camConfig.ImageSize.Height / 2; var imageWidthChild = _FieldScanParams.GetimageSizeWidthChild(); var imageHeightChild = _FieldScanParams.GetimageSizeHeightChild(); double moveOffsetX = 0; double moveOffsetY = 0; var cameraROIWidth = ((imageWidthChild * _SystemParameter.CameraPixelSize) / _SystemParameter.LensMagnification) / 1000.0; var cameraROIHeight = ((imageHeightChild * _SystemParameter.CameraPixelSize) / _SystemParameter.LensMagnification) / 1000.0; moveOffsetX = ((imgPosX * _SystemParameter.CameraPixelSize) / _SystemParameter.LensMagnification) / 1000.0 - cameraROIWidth * 0.5; moveOffsetY = cameraROIHeight * 0.5 - ((imgPosY * _SystemParameter.CameraPixelSize) / _SystemParameter.LensMagnification) / 1000.0; //Jin : X Filp, Y Flip에 따라서 Offset 수식을 변경해줄 필요 없을 듯.. Flip을 사용하던 안하던 Scanner 입장에서는 동일할테니깐 //if (camConfig.FlipAndRotation.ImageFlip == eImageFlip.LeftToRight) { // //X Flip // moveOffsetX = cameraROIWidth * 0.5 - ((imgPosX * _SystemParameter.CameraPixelSize) / _SystemParameter.LensMagnification) / 1000.0; //} else { // moveOffsetX = ((imgPosX * _SystemParameter.CameraPixelSize) / _SystemParameter.LensMagnification) / 1000.0 - cameraROIWidth * 0.5; //} //if (camConfig.FlipAndRotation.ImageFlip == eImageFlip.TopToBottom) { // //Y Flip // moveOffsetY = ((imgPosY * _SystemParameter.CameraPixelSize) / _SystemParameter.LensMagnification) / 1000.0 - cameraROIHeight * 0.5; //} else { // moveOffsetY = cameraROIHeight * 0.5 - ((imgPosY * _SystemParameter.CameraPixelSize) / _SystemParameter.LensMagnification) / 1000.0; //} //angle은 현재 사용하지 않네.. //double angle = GrabAngle * Math.PI / 180; double angle = 0; realPosX = moveOffsetX * Math.Cos(angle) + moveOffsetY * Math.Sin(angle); realPosY = -moveOffsetX * Math.Sin(angle) + moveOffsetY * Math.Cos(angle); } private string DecisionCheckOffset(SequenceContext context, string id, ICollection branches, SequenceActionResult param) { Log.I(string.Format($"Find Pattern Result Offset:[{_PatternOffsetX},{_PatternOffsetY}] Score:[{_Score}]")); // Pattern Find OK if (Math.Max(Math.Abs(_PatternOffsetX), Math.Abs(_PatternOffsetY)) < _InitParam.CheckOffset) { Log.FuncI(string.Format($"Check Offset Result True Criteria:[{_InitParam.CheckOffset}] Max Offset:[{Math.Max(Math.Abs(_PatternOffsetX), Math.Abs(_PatternOffsetY))}")); return SEQ_SET_VISION_POSITION; } // Pattern Find nOK >> 설정 범위 넘어감 else if (_FindPatternCount < 10) { _CheckOffsetCount++; Log.FuncI(string.Format($"Find Pattern Count[{_FindPatternCount}]")); return SEQ_FIND_PATTERN; } else { Log.E(String.Format("ERROR_CHECK_OFFSET_COUNT_OVER")); return SEQ_SET_VISION_POSITION_FAILED; } } private SequenceActionResult SetVisionPosition(SequenceContext context, string id, SequenceActionResult param) { double currentX, currentY; //_Scanner.GetXYPos(out currentX, out currentY); _Scanner.GetXYPos(out currentX, out currentY); if (_InitParam.IsVisionCalibration) { // Vision Scanner Calibration인 경우 int visionBitX = (int)(currentX * _Scanner.Config.ScaleXYFactor); int visionBitY = (int)(currentY * _Scanner.Config.ScaleXYFactor); //DataGrid에 Vision 좌표 업데이트 //ViewModel에서 이벤트 생성하고 시퀀스에서 이벤트로 주고 TreadSleep 줘보자.. 몇초 줘야하는지는 확인해보자 var updateDataGridItem = new UpdateDataGridItem(_PositionIndex, visionBitX, visionBitY); context.EventPublisher.Publish(new EventMessage(context.SequenceId, context.SequenceId, updateDataGridItem)); Thread.Sleep(100); Log.I(string.Format($"UPDATE_VISION_CORRECTION_TABLE;{_PositionIndex};{visionBitX};{visionBitY}")); } else { // Laser Scanner Calibration인 경우 int visionBitX = (int)(((_CurrentPositionX - _InitParam.MasterPosOffsetX) * 2 - currentX) * _Scanner.Config.ScaleXYFactor); int visionBitY = (int)(((_CurrentPositionY - _InitParam.MasterPosOffsetY) * 2 - currentY) * _Scanner.Config.ScaleXYFactor); var updateDataGridItem = new UpdateDataGridItem(_PositionIndex, (int)visionBitX, (int)visionBitY); context.EventPublisher.Publish(new EventMessage(context.SequenceId, context.SequenceId, updateDataGridItem)); Thread.Sleep(100); Log.I(string.Format($"UPDATE_LASER_CORRECTION_TABLE;{_PositionIndex};{visionBitX};{visionBitY}")); } return SequenceAction.Ok(); } private SequenceActionResult SetVisionPositionFailed(SequenceContext context, string id, SequenceActionResult param) { if (_InitParam.IsVisionCalibration) { //DataGrid에 Vision 좌표 업데이트 //ViewModel에서 이벤트 생성하고 시퀀스에서 이벤트로 주고 TreadSleep 줘보자.. 몇초 줘야하는지는 확인해보자 var updateDataGridItem = new UpdateDataGridItem(_PositionIndex, 0, 0); context.EventPublisher.Publish(new EventMessage(context.SequenceId, context.SequenceId, updateDataGridItem)); Thread.Sleep(100); Log.I(string.Format($"UPDATE_VISION_CORRECTION_TABLE;{_PositionIndex};{0};{0}")); } else { // Laser Scanner Calibration인 경우 var updateDataGridItem = new UpdateDataGridItem(_PositionIndex, 0, 0); context.EventPublisher.Publish(new EventMessage(context.SequenceId, context.SequenceId, updateDataGridItem)); Thread.Sleep(100); Log.I(string.Format($"UPDATE_VISION_CORRECTION_TABLE;{_PositionIndex};{0};{0}")); } return SequenceAction.Ok(); } private string DecisionCheckNextPostionExists(SequenceContext context, string id, ICollection branches, SequenceActionResult param) { if (_InitParam.Is1StepCalibration) { return SEQ_SAVE_RESULT_DATA; } else { _PositionIndex++; if (_PositionIndex < _MarkingPositionList_MM.Count) { return SEQ_CAPTURE_NEXT_POSITION; } else { return SEQ_SAVE_RESULT_DATA; } } } private SequenceActionResult SaveResultData(SequenceContext context, string id, SequenceActionResult param) { // 결과 Data 를 업데이트 함. format 유지, ScannerCalibration 없음 context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, true); return SequenceAction.Ok(); } private SequenceActionResult WriteCompletionPLCBit(SequenceContext context, string id, SequenceActionResult param) { // 완료 비트 write 하는 Node 추가 필요. format 유지, ScannerCalibration 없음 Log.D("ScannerCalibration Sequence WriteCompletionPLCBit"); return SequenceAction.Ok(); } private SequenceActionResult Complete(SequenceContext context, string id, SequenceActionResult param) { RestoreDeviceStatus(); Log.I("ScannerCalibration Complete"); context.TryGetValue(ContextKey.KEY_SEQUENCE_RESULT, out bool seqResult); SeqScannerCalibration_OutParam result = new SeqScannerCalibration_OutParam() { Success = seqResult }; if (seqResult == false) { AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_ScannerCalibration_ResultFailed); } Log.D("ScannerCalibration Sequence Complete"); return SequenceAction.Ok(result); } private bool RestoreDeviceStatus() { //vision correctiontable로 원복 _Scanner.SelectCorrectionTable(1); //Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨 _Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND); //Scanner 원점 복귀 _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND); return true; } private void OnForceError(SequenceContext arg1, string arg2) { //List 실행 종료 _Scanner.StopExecution(); //Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨 _Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND); //Scanner 원점 복귀 _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND); Log.E("ScannerCalibration Sequence is stopped"); } private void OnStopped(SequenceContext obj) { //List 실행 종료 _Scanner.StopExecution(); //Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨 _Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND); //Scanner 원점 복귀 _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND); Log.E("ScannerCalibration Sequence is stopped"); } } public class SeqScannerCalibration_InitParam : InitParam { public FovInfo FovInfo { get; set; } //public eDisplayType _DisplayType { get; set; } public bool IsVisionCalibration { get; set; } public int ColumnCount { get; set; } public int RowCount { get; set; } public float UnitLength { get; set; } public float TiltingDegree { get; set; } public double CheckOffset { get; set; } public double MasterPosOffsetX { get; set; } public double MasterPosOffsetY { get; set; } public bool Is1StepCalibration { get; set; } public int RetryNum { get; set; } public int SelectedRowIndex { get; set; } } public class SeqScannerCalibration_OutParam : OutParam { // TODO: 몇개가 Vision Fail 되었는지가 나오면 좋음. } }