diff --git a/Machines/ILSOS_PrismaticRD_PTOP/Views/Main/UCMainAutoRunViewModel.cs b/Machines/ILSOS_PrismaticRD_PTOP/Views/Main/UCMainAutoRunViewModel.cs index 56bd333..2004e8f 100644 --- a/Machines/ILSOS_PrismaticRD_PTOP/Views/Main/UCMainAutoRunViewModel.cs +++ b/Machines/ILSOS_PrismaticRD_PTOP/Views/Main/UCMainAutoRunViewModel.cs @@ -109,8 +109,10 @@ namespace ILSOS_PrismaticRD_PTOP.Views CMD_VisibleChanged = new DelegateCommand(VisibleChanged); - CMD_RunSequenceAutoRun = new DelegateCommand(OnAutoRunStartRequest); // 260511 이곳 시퀸스 - CMD_StopSequenceAutoRun = new DelegateCommand(OnStopSequenceAutoRun); + // 260813: 자동시작 요청 출처를 넘긴다 — OnAutoRunStartRequest 는 버튼/PLC 이벤트 공용 진입점이라 + // 진입부 로그가 출처를 단정하면 안 된다(기존 문구가 버튼 클릭도 "AUTO_REQ"로 찍었다). + CMD_RunSequenceAutoRun = new DelegateCommand(o => OnAutoRunStartRequest("Button")); // 260511 이곳 시퀸스 + CMD_StopSequenceAutoRun = new DelegateCommand(OnStopSequenceAutoRun); // 260813: 정지 시 에코 워드 0 초기화 재적용(조건부) — ResumeMode/M70515 구간만 보존, SeqAutoRunPTOP.OnSeqActionStop 260813 region CMD_Reset = new DelegateCommand(PerformCmdReset); CMD_AutoRunResume = new DelegateCommand(OnAutoRunResume); @@ -121,7 +123,7 @@ namespace ILSOS_PrismaticRD_PTOP.Views //이벤트로 AUTORUN 스타트 요청에 대한 메소드 실행 오면 그때 동작함. 시퀸스 돔 _Aurora.EventObservable.AsObservable(CommonEventIds.EVENT_AUTO_RUN_START_REQUEST) .ObserveOnDispatcher() - .Subscribe(x => OnAutoRunStartRequest()); + .Subscribe(x => OnAutoRunStartRequest("PLC_AUTO_REQ")); // 260813: 출처 명시(발행처는 PLCController.MonitorPlcInterfaceMap 뿐) // Stop 요청 시 정지 로직 실행 _Aurora.EventObservable.AsObservable(CommonEventIds.EVENT_AUTO_RUN_STOP_REQUEST) @@ -208,6 +210,25 @@ namespace ILSOS_PrismaticRD_PTOP.Views // 음극/양극 어느 쪽이 줘도 트리거 (AutoRun이 두 극을 함께 쓰는 구조와 동일) bool bit15 = _Plc.ReadBit(eBitInput.PLC_WELD_READY_REQ).data; IsModeChangeActive = bit15; // bit15 살아있을 때만 [AutoRun Resume] 버튼 표시 + + #region 260820 bit15 구간 Recipe/Page 를 Recipe 작업 화면에도 표시 (원복: 이 region 삭제) + // 팝업은 상승엣지 1회라 [확인] 을 누르면 값이 사라진다. 현장 소음 속에서 작업자가 + // 몇 번 레시피 몇 페이지였는지 잊는다는 요청으로, Recipe 작업 화면 상단에도 같은 값을 띄운다. + // · 트리거: 팝업과 같은 bit15. 단 팝업은 엣지 1회, 표시는 ON 구간 내내 레벨 유지. + // · 값: 팝업이 읽는 두 워드 그대로(PLC 원본 입력). PC 에코(PC_*)가 아니다. + // 공용(WeldingCommon)은 Status 만 읽고 PTOP 을 참조하지 않는다 — 타 설비는 값이 없어 자동 미표시. + // 분석: docs/분석/2026/08/20/RecipePage_현장표시_레이아웃_분석.md + if (bit15) + { + Status.PlcGuideRecipeNo = _Plc.ReadWord(eWordInput.PLC_RECIPE_NO).data; + Status.PlcGuidePageNo = _Plc.ReadWord(eWordInput.PLC_PAGE_NO).data; + } + else if (Status.PlcGuideRecipeNo != null) + { + Status.PlcGuideRecipeNo = null; + Status.PlcGuidePageNo = null; + } + #endregion if (bit15 && !_prevBit15 && !_isPopupOpen) { short r = _Plc.ReadWord(eWordInput.PLC_RECIPE_NO).data; @@ -292,7 +313,9 @@ namespace ILSOS_PrismaticRD_PTOP.Views // 이어하기는 전용 [AutoRun Resume] 버튼(OnAutoRunResume) 경로라 이 가드와 무관.) if (_Plc != null && !_Plc.IsSimul && _Plc.ReadBit(eBitInput.PLC_WELD_READY_REQ).data) { - Log.I("[Bit15] bit15 ON 중 → AUTO_REQ 자동시작 무시 (Manual 유지)"); + // 260813: 출처를 같이 남긴다. 이 메서드는 [운전 시작] 버튼과 PLC 이벤트가 공용으로 쓰는 + // 진입점이라, 여기서 "AUTO_REQ"로 단정하면 버튼 클릭도 PLC 신호로 오독된다. + Log.I($"[Bit15] bit15 ON 중 → 자동시작 요청 무시 (요청출처:{obj ?? "unknown"}, Manual 유지)"); return; } @@ -428,9 +451,6 @@ namespace ILSOS_PrismaticRD_PTOP.Views //if (_SeqAutoRun.RunState == SequenceRunState.Running) // _SeqAutoRun.Stop(); - - - if (_SeqAutoRunPTOP.RunState == SequenceRunState.Running) _SeqAutoRunPTOP.Stop(); diff --git a/WeldingComponents/WeldingCommon/Status.cs b/WeldingComponents/WeldingCommon/Status.cs index 6c84d86..8b99655 100644 --- a/WeldingComponents/WeldingCommon/Status.cs +++ b/WeldingComponents/WeldingCommon/Status.cs @@ -52,5 +52,15 @@ namespace WeldingCommon } public static bool RMSUse { get; set; } = false; + + #region 260820 PLC 지시 Recipe/Page (원복: 이 region 삭제) + // 설비별 시퀀스/VM 이 값을 넣고, 공용 Recipe 작업 화면이 읽어서 상단 띠에 표시한다. + // PTOP: UCMainAutoRunViewModel.OnMonitoring() 에서 bit15(M70515) ON 구간에만 대입, OFF 면 null. + // 여기에 두는 이유 — 공용 화면이 설비(PTOP)를 참조하지 않게 하려면 의존 방향을 뒤집어야 한다. + // 값을 넣지 않는 설비는 계속 null 이므로 분기문 없이 자동으로 미표시된다. + // ※ null = 표시 안 함. 0 은 "PLC 가 0을 줬다"는 유효한 상태라 null 과 구분한다. + public static short? PlcGuideRecipeNo { get; set; } + public static short? PlcGuidePageNo { get; set; } + #endregion } } diff --git a/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeaching.xaml b/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeaching.xaml index f8d0b55..b6613a7 100644 --- a/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeaching.xaml +++ b/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeaching.xaml @@ -18,13 +18,35 @@ --> + + + + + + + + + - - + + + + + + + + diff --git a/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBoxViewModel.cs b/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBoxViewModel.cs index 9334bec..d4e2f2f 100644 --- a/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBoxViewModel.cs +++ b/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBoxViewModel.cs @@ -82,6 +82,32 @@ namespace WeldingCommon.Views private bool _IsObjectSelectAll; public bool IsObjectSelectAll { get => _IsObjectSelectAll; set => SetProperty(ref _IsObjectSelectAll, value); } + #region 260820 PLC 지시 Recipe/Page 표시 (원복: 이 region + OnMonitoring 의 SyncPlcGuide() 호출 삭제) + // 값은 설비별로 Status 에 들어온다(PTOP: bit15 구간에만). 공용은 Status 만 읽으므로 설비 의존이 없다. + // 바인딩은 UCScannerObjectTeaching.xaml 상단 띠 — 그 화면의 DataContext(바깥 VM)가 + // ScannerObjectTeachingControlBoxViewModel 을 공개하고 있어 여기 프로퍼티를 그대로 쓴다. + // (바깥 VM 에는 폴링 타이머가 없고 이 VM 에는 100ms 타이머가 이미 돌고 있어 재활용한다) + private bool _IsPlcGuideVisible; + public bool IsPlcGuideVisible { get => _IsPlcGuideVisible; set => SetProperty(ref _IsPlcGuideVisible, value); } + + private string _PlcGuideText = string.Empty; + public string PlcGuideText { get => _PlcGuideText; set => SetProperty(ref _PlcGuideText, value); } + + private void SyncPlcGuide() + { + var recipeNo = Status.PlcGuideRecipeNo; + var pageNo = Status.PlcGuidePageNo; + + if (recipeNo == null || pageNo == null) { + IsPlcGuideVisible = false; + return; + } + + PlcGuideText = $"RECIPE {recipeNo} PAGE {pageNo}"; + IsPlcGuideVisible = true; + } + #endregion + // 레시피별 Laser 방향 발진 차단 체크 (Recipe_Laser_Interlock.json) — 변경 시 즉시 활성 레시피에 저장 private bool _IsFrontInterlock; public bool IsFrontInterlock @@ -245,6 +271,8 @@ namespace WeldingCommon.Views private void OnMonitoring(object sender, EventArgs e) { + // 260820: 아래 IO 블록은 미연결 시 return 하므로, PLC 지시 표시는 반드시 그 앞에서 갱신한다. + SyncPlcGuide(); // [기존 소스 보존] Laser 디바이스 기반 Pilot 상태 (DO 3=GuideLaserEnable) — 일부 Laser 미구현 시 항상 false //if (_Laser.GetPilotLaserStatus()) {