using Aurora.Api;
using Aurora.Api.Devices;
using VisionCommon;
using VisionSequence.Pattern;
using VisionAligner;
using Hermes;
using VisionDirector;
using System.Collections.Generic;
using System.Linq;
namespace VisionSequence.Aligner {
///
/// 얼라인 믹서 시퀀스 파라미터
///
public class AlignMixerSeqParams : SeqParamsBase {
/// Target 얼라인 키 정보
public AlignInfo AlignInfo_Tgt { get; set; }
/// Source 얼라인 키 정보
public AlignInfo AlignInfo_Src { get; set; }
/// Target 얼라인 결과
public AlignResult AlignResult_Tgt { get; set; }
/// FOV별로 이동시킬 절대위치
public Dictionary FovPositions { get; set; }
/// 메인 화면 타입
public eDisplayType DisplayType { get; set; }
///
/// 얼라인 재시도(Retry) 카운트 초기화 플래그
/// 1) 비전에서 Move와 Retry를 처리할 경우 [True]
/// 2) 제어에서 Move와 Retry를 처리하면서, 얼라인 시퀀스를 신규 호출한 경우 [True]
/// 3) 제어에서 Move와 Retry를 처리하면서, Retry로 얼라인 시퀀스를 다시 호출한 경우 [False]
///
public bool AlignCntClear { get; set; }
///
/// 얼라인 믹서 시퀀스 파라미터를 얻어온다.
///
/// Target 얼라인 키 정보
/// Source 얼라인 키 정보
/// Target 얼라인 결과가 null이 아닐 경우, 이후 Target 얼라인 스킵 (선택사항)
/// FOV별로 이동시킬 절대위치. null일 경우, 자동으로 Adapter에 정의된 티칭위치로 이동
/// 메인 화면 타입
/// 얼라인 횟수
/// 시퀀스 모드
public AlignMixerSeqParams(
AlignInfo alignInfo_Tgt,
AlignInfo alignInfo_Src,
AlignResult alignResult_Tgt = null,
Dictionary fovPositions = null,
eDisplayType displayType = eDisplayType.Main_1,
bool alignCntClear = true,
eSeqMode seqMode = eSeqMode.Auto) : base(seqMode) {
this.AlignInfo_Tgt = alignInfo_Tgt;
this.AlignInfo_Src = alignInfo_Src;
this.AlignResult_Tgt = alignResult_Tgt;
this.FovPositions = fovPositions;
this.DisplayType = displayType;
this.AlignCntClear = alignCntClear;
}
}
///
/// Target(고정형)과 Source(이동형)의 얼라인 보정량을 조합하여, 최종적으로 Source를 Target에 맞추는 얼라인 시퀀스
/// Target의 3축(X,Y,T)이 모두 움직일 수가 없기 때문에, Source의 모터를 대신 움직여 얼라인 한다.
///
public class AlignMixerSeq : SubSeqBase {
private readonly Logger LOG = new Logger(typeof(AlignMixerSeq).Name);
private Logger _VisionLog = new Logger(typeof(AlignMixerSeq).Name, VisionKeys.LogKey);
private TactTimeChecker _TactTimeChecker;
// Target Global Variables
private SubSysInfo _SubSysInfo_Tgt;
private VisionAligner.Aligner _Aligner_Tgt;
private AlignRecipe _AlignRecipe_Tgt;
private AlignResult _AlignResult_Tgt;
// Source Global Variables
private SubSysInfo _SubSysInfo_Src;
private VisionAligner.Aligner _Aligner_Src;
private AlignRecipe _AlignRecipe_Src;
private AlignResult _AlignResult_Src;
// Mixed Global Variables
private AlignResult _AlignResult_Mixed; // _AlignResult_Tgt + _AlignResult_Src
private AlignLimitChecker _AlignLimitChecker_Mixed;
private AlignRetryChecker _AlignRetryChecker_Mixed;
private AlignMixerSeqParams _Params;
private CsvManager _CsvManager = new CsvManager();
private ImageManager _ImageManager = new ImageManager();
private string _SeqName;
#region Sequence Id
private const string SEQ_ID_INIT = "INIT";
// Dry Run
private const string SEQ_ID_CHECK_DRY_RUN = "CHECK_DRY_RUN";
private const string SEQ_ID_FORK_DRY_RUN = "FORK_DRY_RUN";
private const string SEQ_ID_CHECK_ALIGN_RESULT_DRY_RUN_TGT = "CHECK_ALIGN_RESULT_DRY_RUN_TGT";
private const string SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_TGT = "SET_DRY_RUN_SEQ_PARAMS_TGT";
private const string SEQ_ID_DRY_RUN_SEQ_TGT = "DRY_RUN_SEQ_TGT";
private const string SEQ_ID_DRY_RUN_UPDATE_UI_TGT = "DRY_RUN_UPDATE_UI_TGT";
private const string SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_SRC = "SET_DRY_RUN_SEQ_PARAMS_SRC";
private const string SEQ_ID_DRY_RUN_SEQ_SRC = "DRY_RUN_SEQ_SRC";
private const string SEQ_ID_DRY_RUN_UPDATE_UI_SRC = "DRY_RUN_UPDATE_UI_SRC";
private const string SEQ_ID_JOIN_DRY_RUN = "JOIN_DRY_RUN";
// Align
private const string SEQ_ID_FORK_ALIGN = "FORK_ALIGN";
private const string SEQ_ID_CHECK_ALIGN_RESULT_TGT = "CHECK_ALIGN_RESULT_TGT";
private const string SEQ_ID_CLEAR_TGT = "CLEAR_TGT";
private const string SEQ_ID_SET_ALIGN_SEQ_PARAMS_TGT = "SET_ALIGN_SEQ_PARAMS_TGT";
private const string SEQ_ID_ALIGN_SEQ_TGT = "ALIGN_SEQ_TGT";
private const string SEQ_ID_CHECK_STATUS_TGT = "CHECK_STATUS_TGT";
private const string SEQ_ID_CLEAR_SRC = "CLEAR_SRC";
private const string SEQ_ID_SET_ALIGN_SEQ_PARAMS_SRC = "SET_ALIGN_SEQ_PARAMS_SRC";
private const string SEQ_ID_ALIGN_SEQ_SRC = "ALIGN_SEQ_SRC";
private const string SEQ_ID_CHECK_STATUS_SRC = "CHECK_STATUS_SRC";
private const string SEQ_ID_JOIN_ALIGN = "JOIN_ALIGN";
private const string SEQ_ID_MARK_NG = "MARK_NG";
private const string SEQ_ID_DISTANCE_NG = "DISTANCE_NG";
// Mix Align Data
private const string SEQ_ID_MIX_ALIGN_RESULTS = "MIX_ALIGN_RESULTS";
private const string SEQ_ID_CHECK_STATUS_MIXED = "CHECK_STATUS_MIXED";
private const string SEQ_ID_ALIGN_LIMIT_NG = "ALIGN_LIMIT_NG";
private const string SEQ_ID_RETRY_COUNT_NG = "RETRY_COUNT_NG";
// Apply Offset & Move
private const string SEQ_ID_FORK_MOVE = "FORK_MOVE";
private const string SEQ_ID_APPLY_OFFSET = "APPLY_OFFSET";
private const string SEQ_ID_CHECK_MOVE_ENABLED = "CHECK_MOVE_ENABLED";
private const string SEQ_ID_MOVE_DELTA = "MOVE_DELTA";
private const string SEQ_ID_MOVE_FINISHED = "MOVE_FINISHED";
private const string SEQ_ID_UPDATE_UI = "UPDATE_UI";
private const string SEQ_ID_SAVE_CSV = "SAVE_CSV";
private const string SEQ_ID_SAVE_SCREENSHOT = "SAVE_SCREENSHOT";
private const string SEQ_ID_JOIN_MOVE = "JOIN_MOVE";
// Retry
private const string SEQ_ID_CHECK_RETRY = "CHECK_RETRY";
private const string SEQ_ID_SEQ_COMPLETE = "SEQ_COMPLETE";
#endregion
///
/// 생성자
///
/// 시퀀스 이름
/// Target 얼라인 키 정보
/// Source 얼라인 키 정보
public AlignMixerSeq(string seqName, AlignInfo alignInfo_Tgt, AlignInfo alignInfo_Src) {
_SeqName = seqName;
_AlignRecipe_Tgt = VisionRecipeManager.GetAlignRecipe(alignInfo_Tgt);
_AlignRecipe_Src = VisionRecipeManager.GetAlignRecipe(alignInfo_Src);
}
private void SetActions(SequenceBuilder sb) {
SequenceBuilder dryRunSeqBuilder_tgt = SubSeqManager.CreateSubSeqBuilder(new AlignSeq_DryRun(), typeof(AlignSeq_DryRun).Name);
SequenceBuilder dryRunSeqBuilder_src = SubSeqManager.CreateSubSeqBuilder(new AlignSeq_DryRun(), typeof(AlignSeq_DryRun).Name);
SequenceBuilder alignSeqBuilder_Tgt = SubSeqManager.CreateSubSeqBuilder(new AlignSubSeq(), typeof(AlignSubSeq).Name);
SequenceBuilder alignSeqBuilder_Src = SubSeqManager.CreateSubSeqBuilder(new AlignSubSeq(), typeof(AlignSubSeq).Name);
sb.AddAction(SEQ_ID_INIT, Init);
// Dry Run
sb.AddDecision(SEQ_ID_CHECK_DRY_RUN, CheckDryRun);
sb.AddFork(SEQ_ID_FORK_DRY_RUN);
sb.AddDecision(SEQ_ID_CHECK_ALIGN_RESULT_DRY_RUN_TGT, CheckAlignResult_Tgt);
sb.AddAction(SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_TGT, SetDryRunSeqParams);
sb.AddSubsequence(SEQ_ID_DRY_RUN_SEQ_TGT, dryRunSeqBuilder_tgt);
sb.AddAction(SEQ_ID_DRY_RUN_UPDATE_UI_TGT, UpdateUI);
sb.AddAction(SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_SRC, SetDryRunSeqParams);
sb.AddSubsequence(SEQ_ID_DRY_RUN_SEQ_SRC, dryRunSeqBuilder_src);
sb.AddAction(SEQ_ID_DRY_RUN_UPDATE_UI_SRC, UpdateUI);
sb.AddJoin(SEQ_ID_JOIN_DRY_RUN);
// Align
sb.AddFork(SEQ_ID_FORK_ALIGN);
sb.AddDecision(SEQ_ID_CHECK_ALIGN_RESULT_TGT, CheckAlignResult_Tgt);
sb.AddAction(SEQ_ID_CLEAR_TGT, Clear);
sb.AddAction(SEQ_ID_SET_ALIGN_SEQ_PARAMS_TGT, SetAlignSeqParams);
sb.AddSubsequence(SEQ_ID_ALIGN_SEQ_TGT, alignSeqBuilder_Tgt);
sb.AddDecision(SEQ_ID_CHECK_STATUS_TGT, CheckStatus);
sb.AddAction(SEQ_ID_CLEAR_SRC, Clear);
sb.AddAction(SEQ_ID_SET_ALIGN_SEQ_PARAMS_SRC, SetAlignSeqParams);
sb.AddSubsequence(SEQ_ID_ALIGN_SEQ_SRC, alignSeqBuilder_Src);
sb.AddDecision(SEQ_ID_CHECK_STATUS_SRC, CheckStatus);
sb.AddJoin(SEQ_ID_JOIN_ALIGN);
sb.AddAction(SEQ_ID_MARK_NG, MarkNG);
sb.AddAction(SEQ_ID_DISTANCE_NG, DistanceNG);
// Mix Align Data
sb.AddAction(SEQ_ID_MIX_ALIGN_RESULTS, MixAlignResults);
sb.AddDecision(SEQ_ID_CHECK_STATUS_MIXED, CheckStatusMixed);
sb.AddAction(SEQ_ID_ALIGN_LIMIT_NG, AlignLimitNG);
sb.AddAction(SEQ_ID_RETRY_COUNT_NG, RetryCountNG);
// Move
sb.AddFork(SEQ_ID_FORK_MOVE);
sb.AddAction(SEQ_ID_APPLY_OFFSET, ApplyOffset);
sb.AddDecision(SEQ_ID_CHECK_MOVE_ENABLED, CheckMoveEnabled);
sb.AddAction(SEQ_ID_MOVE_DELTA, MoveDelta);
sb.AddAction(SEQ_ID_MOVE_FINISHED, MoveFinished);
sb.AddAction(SEQ_ID_UPDATE_UI, UpdateUI);
sb.AddAction(SEQ_ID_SAVE_CSV, SaveCsv);
sb.AddAction(SEQ_ID_SAVE_SCREENSHOT, SaveScreenShot);
sb.AddJoin(SEQ_ID_JOIN_MOVE);
// Retry
sb.AddDecision(SEQ_ID_CHECK_RETRY, CheckRetry);
sb.AddAction(SEQ_ID_SEQ_COMPLETE, SeqComplete);
}
public override SequenceBuilder Builder(string seqName) {
_SeqName = seqName;
_TactTimeChecker = new TactTimeChecker(_SeqName);
SequenceBuilder sb = new SequenceBuilder(1000);
SetActions(sb);
sb.OnStopped(OnStopped);
sb.OnForceError(OnForceError);
sb.Flows(
sb.Init
> sb[SEQ_ID_INIT]
> sb[SEQ_ID_CHECK_DRY_RUN] // DryRun인 경우
> sb[SEQ_ID_FORK_DRY_RUN]
> sb[SEQ_ID_CHECK_ALIGN_RESULT_DRY_RUN_TGT]
> sb[SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_TGT]
> sb[SEQ_ID_DRY_RUN_SEQ_TGT]
> sb[SEQ_ID_DRY_RUN_UPDATE_UI_TGT]
> sb[SEQ_ID_JOIN_DRY_RUN]
> sb[SEQ_ID_SEQ_COMPLETE]
);
sb.Flows(
sb[SEQ_ID_CHECK_ALIGN_RESULT_DRY_RUN_TGT]
> sb[SEQ_ID_DRY_RUN_UPDATE_UI_TGT]
);
sb.Flows(
sb[SEQ_ID_FORK_DRY_RUN]
> sb[SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_SRC]
> sb[SEQ_ID_DRY_RUN_SEQ_SRC]
> sb[SEQ_ID_DRY_RUN_UPDATE_UI_SRC]
> sb[SEQ_ID_JOIN_DRY_RUN]
);
sb.Flows(
sb[SEQ_ID_CHECK_DRY_RUN] // DryRun이 아닌 경우
> sb[SEQ_ID_FORK_ALIGN]
> sb[SEQ_ID_CLEAR_SRC]
> sb[SEQ_ID_SET_ALIGN_SEQ_PARAMS_SRC]
> sb[SEQ_ID_ALIGN_SEQ_SRC]
> sb[SEQ_ID_CHECK_STATUS_SRC]
> sb[SEQ_ID_JOIN_ALIGN]
> sb[SEQ_ID_MIX_ALIGN_RESULTS]
> sb[SEQ_ID_CHECK_STATUS_MIXED]
> sb[SEQ_ID_FORK_MOVE]
> sb[SEQ_ID_APPLY_OFFSET]
> sb[SEQ_ID_CHECK_MOVE_ENABLED]
> sb[SEQ_ID_MOVE_DELTA]
> sb[SEQ_ID_MOVE_FINISHED]
> sb[SEQ_ID_JOIN_MOVE]
> sb[SEQ_ID_CHECK_RETRY]
> sb[SEQ_ID_SEQ_COMPLETE]
);
sb.Flows(
sb[SEQ_ID_FORK_ALIGN]
> sb[SEQ_ID_CHECK_ALIGN_RESULT_TGT]
);
sb.Flows(
sb[SEQ_ID_CHECK_ALIGN_RESULT_TGT] // Target 얼라인을 수행하지 않은 경우 Skip (X)
> sb[SEQ_ID_CLEAR_TGT]
> sb[SEQ_ID_SET_ALIGN_SEQ_PARAMS_TGT]
> sb[SEQ_ID_ALIGN_SEQ_TGT]
> sb[SEQ_ID_CHECK_STATUS_TGT]
> sb[SEQ_ID_JOIN_ALIGN]
);
sb.Flows(
sb[SEQ_ID_CHECK_ALIGN_RESULT_TGT] // Target 얼라인을 이미 수행한 경우 Skip (O)
> sb[SEQ_ID_CHECK_STATUS_TGT]
);
sb.Flows(
sb[SEQ_ID_CHECK_STATUS_TGT] // Target Align - Mark NG
> sb[SEQ_ID_MARK_NG]
> sb[SEQ_ID_SEQ_COMPLETE]
);
sb.Flows(
sb[SEQ_ID_CHECK_STATUS_TGT] // Target Align - Distance NG
> sb[SEQ_ID_DISTANCE_NG]
> sb[SEQ_ID_SEQ_COMPLETE]
);
sb.Flows(
sb[SEQ_ID_CHECK_STATUS_SRC] // Retry 후에는 Join할 필요 없이 바로 얼라인 데이터를 Mix시킨다.
> sb[SEQ_ID_MIX_ALIGN_RESULTS]
);
sb.Flows(
sb[SEQ_ID_CHECK_STATUS_SRC] // Source Align - Mark NG
> sb[SEQ_ID_MARK_NG]
> sb[SEQ_ID_SEQ_COMPLETE]
);
sb.Flows(
sb[SEQ_ID_CHECK_STATUS_SRC] // Source Align - Distance NG
> sb[SEQ_ID_DISTANCE_NG]
> sb[SEQ_ID_SEQ_COMPLETE]
);
sb.Flows(
sb[SEQ_ID_CHECK_STATUS_MIXED] // Mixed Align - Limit NG
> sb[SEQ_ID_ALIGN_LIMIT_NG]
> sb[SEQ_ID_SEQ_COMPLETE]
);
sb.Flows(
sb[SEQ_ID_CHECK_STATUS_MIXED] // Mixed Align - Retry Count NG
> sb[SEQ_ID_RETRY_COUNT_NG]
> sb[SEQ_ID_SEQ_COMPLETE]
);
sb.Flows(
sb[SEQ_ID_CHECK_MOVE_ENABLED]
> sb[SEQ_ID_MOVE_FINISHED]
);
sb.Flows(
sb[SEQ_ID_FORK_MOVE]
> sb[SEQ_ID_UPDATE_UI]
> sb[SEQ_ID_SAVE_CSV]
> sb[SEQ_ID_SAVE_SCREENSHOT]
> sb[SEQ_ID_JOIN_MOVE]
);
sb.Flows(
sb[SEQ_ID_CHECK_RETRY] // Retry
> sb[SEQ_ID_CLEAR_SRC]
);
sb.Flows(
sb[SEQ_ID_SEQ_COMPLETE]
> sb.Final
);
#if SEQ_DRAW
sb.Draw($"{GetType().Name}.pdf");
#endif
return sb;
}
private SequenceActionResult Init(
SequenceContext context,
string id,
SequenceActionResult param) {
_TactTimeChecker.Start(TactTimeType.ProcSeq, typeof(AlignMixerSeq).Name);
if (param.Param != null) {
_Params = param.Param as AlignMixerSeqParams;
}
else {
LOG.E($"Param is null.");
return null;
}
_SubSysInfo_Tgt = VisionRecipeManager.GetSubSysInfo(_Params.AlignInfo_Tgt.UnitId, _Params.AlignInfo_Tgt.SubSysId);
_Aligner_Tgt = AlignAgentManager.GetAlignAgent(_Params.AlignInfo_Tgt);
_Aligner_Tgt.Clear();
_AlignRecipe_Tgt = VisionRecipeManager.GetAlignRecipe(_Params.AlignInfo_Tgt);
_AlignResult_Tgt = _Params.AlignResult_Tgt;
_SubSysInfo_Src = VisionRecipeManager.GetSubSysInfo(_Params.AlignInfo_Src.UnitId, _Params.AlignInfo_Src.SubSysId);
_Aligner_Src = AlignAgentManager.GetAlignAgent(_Params.AlignInfo_Src);
_Aligner_Src.Clear();
_AlignRecipe_Src = VisionRecipeManager.GetAlignRecipe(_Params.AlignInfo_Src);
_AlignResult_Src = null;
_AlignLimitChecker_Mixed = new AlignLimitChecker(_AlignRecipe_Src);
_AlignRetryChecker_Mixed = new AlignRetryChecker(_AlignRecipe_Src);
_AlignResult_Mixed = null;
if (_Params.AlignCntClear) {
_Aligner_Tgt.Clear();
_Aligner_Src.Clear();
}
_VisionLog.V("[START]");
return SequenceAction.Ok();
}
private SequenceActionResult Clear(
SequenceContext context,
string id,
SequenceActionResult param) {
if (id == SEQ_ID_CLEAR_TGT) {
//_AlignResult_Tgt = null; // Target 얼라인 데이터는 한 번 정해지면 초기화할 필요가 없다.
GrabFindSeqUtil.Clear(_SubSysInfo_Tgt);
AlignResultItemManager.Clear(_SubSysInfo_Tgt);
PatternResultItemManager.Clear(_SubSysInfo_Tgt);
}
else {
_AlignResult_Src = null;
GrabFindSeqUtil.Clear(_SubSysInfo_Src);
AlignResultItemManager.Clear(_SubSysInfo_Src);
PatternResultItemManager.Clear(_SubSysInfo_Src);
}
return SequenceAction.Ok();
}
#region Dry Run
private string CheckDryRun(
SequenceContext context,
string id,
ICollection branches,
SequenceActionResult param) {
if (EquipAdapter.Instance.EquipMode.IsDryrunOrTransferMode()) {
return SEQ_ID_FORK_DRY_RUN;
}
else {
return SEQ_ID_FORK_ALIGN;
}
}
private SequenceActionResult SetDryRunSeqParams(
SequenceContext context,
string id,
SequenceActionResult param) {
SubSysInfo subSysInfo;
AlignInfo alignInfo;
if (id == SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_TGT) {
subSysInfo = _SubSysInfo_Tgt;
alignInfo = _Params.AlignInfo_Tgt;
}
else {
subSysInfo = _SubSysInfo_Src;
alignInfo = _Params.AlignInfo_Src;
}
var fovInfos = new Dictionary();
foreach (var fovInfo in VisionRecipeManager.GetFovInfos(subSysInfo)) {
fovInfos.Add(fovInfo.FovId, fovInfo);
}
return SequenceAction.Ok(new AlignSeqParams_DryRun(alignInfo, fovInfos));
}
#endregion
private string CheckAlignResult_Tgt(
SequenceContext context,
string id,
ICollection branches,
SequenceActionResult param) {
if (_Params.AlignResult_Tgt == null) {
if (id.Contains("DRY_RUN")) {
return SEQ_ID_DRY_RUN_UPDATE_UI_TGT;
}
else {
return SEQ_ID_CLEAR_TGT; // 입력받은 Target 얼라인 보정량이 없으면, 얼라인 수행
}
}
else {
if (id.Contains("DRY_RUN")) {
return SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_TGT;
}
else {
return SEQ_ID_CHECK_STATUS_TGT; // 입력받은 Target 얼라인 보정량이 있으면, 얼라인 Skip
}
}
}
private SequenceActionResult SetAlignSeqParams(
SequenceContext context,
string id,
SequenceActionResult param) {
if (id == SEQ_ID_SET_ALIGN_SEQ_PARAMS_TGT) {
return SequenceAction.Ok(new AlignSubSeqParams(_Params.AlignInfo_Tgt, _Params.FovPositions));
}
else {
return SequenceAction.Ok(new AlignSubSeqParams(_Params.AlignInfo_Src, _Params.FovPositions));
}
}
private string CheckStatus(
SequenceContext context,
string id,
ICollection branches,
SequenceActionResult param) { // Param : AlignResult
var alignResult = param.Param as AlignResult;
if (id == SEQ_ID_CHECK_ALIGN_RESULT_TGT) {
return SEQ_ID_JOIN_ALIGN; // Target Align - Skip
}
else if (id == SEQ_ID_CHECK_STATUS_TGT) {
if (alignResult != null) {
_AlignResult_Tgt = alignResult;
BindProcessData(context, id, param);
if (_AlignResult_Tgt.Status == AlignStatus.NOT_FOUND) {
return SEQ_ID_MARK_NG;
}
else if (_AlignResult_Tgt.Status == AlignStatus.DISTANCE_NG) {
return SEQ_ID_DISTANCE_NG;
}
else {
return SEQ_ID_JOIN_ALIGN; // 새로 수행한 Target Align - OK
}
}
else {
return SEQ_ID_JOIN_ALIGN; // InitParam으로 넘겨받은 Target Align - OK
}
}
else {
_AlignResult_Src = alignResult;
// Source 자체의 Retry, Limit 체크는 의미가 없기 때문에, 항상 OK로 처리
if (_AlignResult_Src.Status == AlignStatus.RETRY ||
_AlignResult_Src.Status == AlignStatus.RETRY_COUNT_OVER ||
_AlignResult_Src.Status == AlignStatus.ALIGN_LIMIT_OVER) {
_AlignResult_Src.Status = AlignStatus.OK;
}
BindProcessData(context, id, param);
if (_AlignResult_Src.Status == AlignStatus.NOT_FOUND) {
return SEQ_ID_MARK_NG;
}
else if (_AlignResult_Src.Status == AlignStatus.DISTANCE_NG) {
return SEQ_ID_DISTANCE_NG;
}
else {
if (_AlignResult_Mixed?.Status == AlignStatus.RETRY) {
return SEQ_ID_MIX_ALIGN_RESULTS; // Retry Source Align - OK, Join으로 Target을 기다릴 필요가 없다.
}
else {
return SEQ_ID_JOIN_ALIGN; // 최초 1번 Source Align - OK
}
}
}
}
private SequenceActionResult MarkNG(
SequenceContext context,
string id,
SequenceActionResult param) {
return SequenceAction.Error(E1.GENERIC);
}
private SequenceActionResult DistanceNG(
SequenceContext context,
string id,
SequenceActionResult param) {
return SequenceAction.Error(E1.GENERIC);
}
private SequenceActionResult MixAlignResults(
SequenceContext context,
string id,
SequenceActionResult param) {
if (_AlignResult_Tgt != null && _AlignResult_Src != null) {
_AlignResult_Mixed = _AlignResult_Src.Clone();
_AlignResult_Mixed.Delta.X += _AlignResult_Tgt.Delta.X;
_AlignResult_Mixed.Delta.Y += _AlignResult_Tgt.Delta.Y;
_AlignResult_Mixed.Delta.T += _AlignResult_Tgt.Delta.T;
_AlignResult_Mixed.AlignName = "Mixed Align";
}
return SequenceAction.Ok();
}
private string CheckStatusMixed(
SequenceContext context,
string id,
ICollection branches,
SequenceActionResult param) {
// Mixed는 Source의 레시피를 활용하지만, Source의 Status를 그대로 사용할 수 없다.
// Mixed는 Aligner를 사용하지 않기 때문에, Mixed의 Status를 여기서 재정의 해주어야 한다.
_AlignResult_Mixed.Status = AlignStatus.OK;
if (_AlignLimitChecker_Mixed.IsEnabled()) {
_AlignResult_Mixed.Status = _AlignLimitChecker_Mixed.GetAlignLimitStatus(_AlignResult_Mixed.Delta);
if (_AlignResult_Mixed.Status == AlignStatus.ALIGN_LIMIT_OVER) {
return SEQ_ID_ALIGN_LIMIT_NG;
}
}
if (_AlignRetryChecker_Mixed.IsEnabled()) {
_AlignResult_Mixed.Status = _AlignRetryChecker_Mixed.GetRetryStatus(_AlignResult_Mixed.Delta);
if (_AlignResult_Mixed.Status == AlignStatus.RETRY_COUNT_OVER) {
return SEQ_ID_RETRY_COUNT_NG;
}
}
return SEQ_ID_FORK_MOVE;
}
private SequenceActionResult AlignLimitNG(
SequenceContext context,
string id,
SequenceActionResult param) {
BindProcessData(context, id, param);
return SequenceAction.Error(E1.GENERIC);
}
private SequenceActionResult RetryCountNG(
SequenceContext context,
string id,
SequenceActionResult param) {
BindProcessData(context, id, param);
return SequenceAction.Error(E1.GENERIC);
}
private void BindProcessData(
SequenceContext context,
string id,
SequenceActionResult param) {
UpdateUI(context, id, param);
SaveCsv(context, id, param);
SaveScreenShot(context, id, param);
}
private SequenceActionResult ApplyOffset(
SequenceContext context,
string id,
SequenceActionResult param) {
// Retry일 때는 오프셋을 반영하지 않는다.
if (_AlignResult_Mixed.Status == AlignStatus.OK || _AlignResult_Mixed.Status == AlignStatus.F_OK) {
if (_AlignRecipe_Src.AlignOffset.Enabled) { // Mixer 서브시스템이 따로 없으므로, Source의 얼라인 레시피를 사용하기로 한다.
var alignOffset = _AlignRecipe_Src.AlignOffset;
_AlignResult_Mixed.Delta.X += alignOffset.OffsetX;
_AlignResult_Mixed.Delta.Y += alignOffset.OffsetY;
_AlignResult_Mixed.Delta.T.Degree += alignOffset.OffsetT;
}
}
return SequenceAction.Ok();
}
private string CheckMoveEnabled(
SequenceContext context,
string id,
ICollection branches,
SequenceActionResult param) {
if (_AlignRecipe_Src.ProcParams.AlignDeltaMoveEnabled && _Params.SeqMode == eSeqMode.Auto) {
return SEQ_ID_MOVE_DELTA;
}
else {
return SEQ_ID_MOVE_FINISHED; // Skip Move
}
}
private SequenceActionResult MoveDelta(
SequenceContext context,
string id,
SequenceActionResult param) {
if (EquipAdapter.Instance.EquipMode.IsSimulMode()) {
return SequenceAction.Ok();
}
var alignDirector = AlignDirectorManager.Instance.GetDirector(_SubSysInfo_Src);
var motionDelta = alignDirector.ApplyDirection(_AlignResult_Mixed.Delta.ToPosition());
var res = EquipAdapter.Instance.AlignMotionManager.RelMove(_SubSysInfo_Src, motionDelta);
if (res != E1.OK) {
LOG.E("Move Failed.");
return SequenceAction.Error(E2.ABORTED);
}
return SequenceAction.Ok();
}
private SequenceActionResult MoveFinished(
SequenceContext context,
string id,
SequenceActionResult param) {
// Join_Move를 위해 지나가는 노드
return SequenceAction.Ok();
}
private SequenceActionResult UpdateUI(
SequenceContext context,
string id,
SequenceActionResult param) {
AlignResult alignResult;
AlignInfo alignInfo;
if (id.Contains("DRY_RUN")) {
return SequenceAction.Ok();
}
else if (id.Contains("TGT")) {
alignResult = _AlignResult_Tgt;
alignInfo = _Params.AlignInfo_Tgt;
}
else if (id.Contains("SRC")) {
alignResult = _AlignResult_Src;
alignInfo = _Params.AlignInfo_Src;
}
else {
alignResult = _AlignResult_Mixed;
alignInfo = _Params.AlignInfo_Src;
}
VisionEnv.AuroraEnv.EventPublisher.Publish(
new EventMessage(alignInfo.ToSubSysInfo().GetKey(), VisionEventId.UPDATE_ALIGN_RESULT, alignResult));
PrintVisionLog(alignResult);
return SequenceAction.Ok();
}
private SequenceActionResult SaveCsv(
SequenceContext context,
string id,
SequenceActionResult param) {
if (EquipAdapter.Instance.EquipMode.IsNotDryrunAndTransferMode()) {
if (id.Contains("TGT")) {
_CsvManager.SaveCsv(_SubSysInfo_Tgt, AlignResultItemManager.GetAlignResultItem(_SubSysInfo_Tgt, _AlignResult_Tgt), eSaveDataType.Align);
}
else if (id.Contains("SRC")) {
_CsvManager.SaveCsv(_SubSysInfo_Src, AlignResultItemManager.GetAlignResultItem(_SubSysInfo_Src, _AlignResult_Src), eSaveDataType.Align);
}
else {
_CsvManager.SaveCsv(_SubSysInfo_Src, AlignResultItemManager.GetAlignResultItem(_SubSysInfo_Src, _AlignResult_Mixed), eSaveDataType.Align);
}
}
return SequenceAction.Ok();
}
private SequenceActionResult SaveScreenShot(
SequenceContext context,
string id,
SequenceActionResult param) {
if (EquipAdapter.Instance.EquipMode.IsNotDryrunAndTransferMode()) {
var materialInfo = context.GetValue(VisionContextKeys.CNTXT_KEY_MATERIAL_INFO);
VisionAligner.Aligner aligner;
AlignResult alignResult;
AlignInfo alignInfo;
SubSysInfo subSysInfo;
if (id.Contains("TGT")) {
aligner = _Aligner_Tgt;
alignResult = _AlignResult_Tgt;
alignInfo = _Params.AlignInfo_Tgt;
subSysInfo = _SubSysInfo_Tgt;
}
else if (id.Contains("SRC")) {
aligner = _Aligner_Src;
alignResult = _AlignResult_Src;
alignInfo = _Params.AlignInfo_Src;
subSysInfo = _SubSysInfo_Src;
}
else {
aligner = _Aligner_Src;
alignResult = _AlignResult_Mixed;
alignInfo = _Params.AlignInfo_Src;
subSysInfo = _SubSysInfo_Src;
}
if (aligner.IsAlignStatusNG(alignResult.Status) || VisionSettingManager.VisionSetting.SaveOkSceenShotImage) {
var lastFovId = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_LAST_MEASURE_FOV_ID, alignInfo.GetKey()));
_ImageManager.SaveScreenShot(
VisionRecipeManager.GetFovInfo(subSysInfo, lastFovId),
alignResult.Status.ToString(), materialInfo, _Params.DisplayType,
AlignResultItemManager.GetTimeStamp(subSysInfo, alignResult));
}
}
return SequenceAction.Ok();
}
private string CheckRetry(
SequenceContext context,
string id,
ICollection branches,
SequenceActionResult param) {
if (_AlignRetryChecker_Mixed.IsEnabled()) {
if (_AlignResult_Mixed.Status == AlignStatus.RETRY) {
if (_AlignRecipe_Src.ProcParams.AlignDeltaMoveEnabled) {
return SEQ_ID_CLEAR_SRC; // RETRY
}
else {
return SEQ_ID_SEQ_COMPLETE; // RETRY, BUT NOT MOVE
}
}
}
return SEQ_ID_SEQ_COMPLETE; // OK
}
private SequenceActionResult SeqComplete(
SequenceContext context,
string id,
SequenceActionResult param) {
_TactTimeChecker.Stop(TactTimeType.ProcSeq, typeof(AlignMixerSeq).Name); // 택타임 측정 종료
_VisionLog.V("[END]");
return SequenceAction.Ok(_AlignResult_Mixed);
}
private void PrintVisionLog(AlignResult alignResult) {
_VisionLog.I($"AlignCnt : {alignResult.AlignCnt}");
_VisionLog.I($"dX : {alignResult.Delta.X:F4}, dY : {alignResult.Delta.Y:F4}, dT : {alignResult.Delta.T.Degree:F4}");
_VisionLog.I($"Status : {alignResult.Status}");
_VisionLog.I($"Algorithm : {alignResult.AlignAlgorithm}");
}
void OnStopped(
SequenceContext context) {
LOG.I($"{context.SequenceId} - STOP Handler");
HandleAbnormalFinish(context);
}
void OnForceError(
SequenceContext context, string id) {
LOG.E($"{context.SequenceId} - FORCE ERROR Handler");
HandleAbnormalFinish(context);
}
void HandleAbnormalFinish(
SequenceContext context) {
_TactTimeChecker.Stop(TactTimeType.ProcSeq, typeof(AlignMixerSeq).Name); // 택타임 측정 종료
}
}
}