증상: 버튼을 한 번 눌러도 반응이 없고 두 번 눌러야 Laser_REMOTE 가 켜진다(현장 확인). 원인: toggleIO 가 "다음에 쓸 값"을 VM 이 기억하는 방식이라 실제 DO 와 어긋난다. SeqAutoRunPTOP 가 같은 DO 를 쓰면서 toggleIO 를 갱신하지 않기 때문 — :1220 용접시작 true / :1243 용접종료 false / :1701 정지 false 어긋나면 버튼이 같은 값을 다시 써서 아무 변화가 없다. 조치: 현재 DO 를 읽어 반전한다(바로 위 PerformCmdLaserGuideBeam :607-608 과 동일 패턴). - 버튼 1회 입력에 TRUE/FALSE 가 정확히 기록된다 - 누가 중간에 DO 를 건드려도 항상 현재 상태 기준으로 동작한다 - 표시(OnMonitoring 의 IsLaserReady = ReadDO(Laser_REMOTE))와 1:1 로 맞는다 - AC_KEY(DO 8)는 전원 투입 계층이라 건드리지 않는다 toggleIO 필드는 원복 대비로 남겨둔다(주석 블록 2개가 참조). 외부 참조·XAML 바인딩 없음. 원복: "260826 이전 원본" region 을 살리고 아래 2줄을 제거. 부수 확인: "뷰 전환 시 REMOTE 꺼짐" 은 VisibleChanged(:251-260)에 DO 쓰기가 없고 시퀀스가 끄는 정상 동작이라 별도 조치하지 않는다.
720 lines
32 KiB
C#
720 lines
32 KiB
C#
using Aurora;
|
|
using Aurora.Api;
|
|
using Aurora.View;
|
|
using AuroraIO;
|
|
using AuroraLaser;
|
|
using AuroraLaserScanner;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Reactive.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Input;
|
|
using System.Windows.Threading;
|
|
using VisionCommon;
|
|
using WeldingCommon;
|
|
using WeldingCommon.Devices.Laser;
|
|
using WeldingCommon.Models;
|
|
using WeldingCommon.Sequences;
|
|
using WeldingCommon.Settings;
|
|
using WeldingCommon.Utils;
|
|
using static AuroraLaser.Ekspla.NL202;
|
|
using static WeldingCommon.Devices.Laser.PAIX_PrismaticScanner;
|
|
|
|
namespace WeldingCommon.Views
|
|
{
|
|
public class UCScannerObjectTeachingControlBoxViewModel : NotifyPropertyChangedBase
|
|
{
|
|
private readonly Logger Log = new Logger("UCScannerObjectTeachingControlBoxViewModel");
|
|
|
|
public ICommand CMD_VisibleChanged { get; }
|
|
|
|
public ICommand CMD_RunScoutNormal { get; }
|
|
public ICommand CMD_StopScoutNormal { get; }
|
|
|
|
public ICommand CMD_RunScoutFlyingVision { get; }
|
|
public ICommand CMD_StopScoutFlyingVision { get; }
|
|
|
|
public ICommand CMD_RunTeachingRun { get; }
|
|
public ICommand CMD_StopTeachingRun { get; }
|
|
|
|
public ICommand CMD_LaserGuideBeam { get; }
|
|
public ICommand CMD_LaserReadyCheck { get; }
|
|
public ICommand CMD_ImportObjectFromDXF { get; }
|
|
public ICommand CMD_ToggleScannerCamView { get; }
|
|
public ICommand CMD_ShowSelectFieldWindow { get; }
|
|
|
|
private readonly IAuroraEnvironment _Aurora;
|
|
private SystemParameter _SystemParameter;
|
|
//private readonly EquipmentInfo _EquipmentInfo;
|
|
|
|
private readonly Scanner _Scanner;
|
|
private readonly Laser _Laser;
|
|
private readonly LaserConfig _LaserConfig;
|
|
private IODevice _IODevice;
|
|
|
|
|
|
private ISequence _SeqScoutNormal;
|
|
private bool _IsSequenceRunning_ScoutNormal;
|
|
public bool IsSequenceRunning_ScoutNormal { get => _IsSequenceRunning_ScoutNormal; set => SetProperty(ref _IsSequenceRunning_ScoutNormal, value); }
|
|
|
|
private ISequence _SeqScoutFlyingVision;
|
|
private bool _IsSequenceRunning_ScoutFlyingVision;
|
|
public bool IsSequenceRunning_ScoutFlyingVision { get => _IsSequenceRunning_ScoutFlyingVision; set => SetProperty(ref _IsSequenceRunning_ScoutFlyingVision, value); }
|
|
|
|
private ISequence _SeqTeachingRun;
|
|
|
|
private bool _IsSequenceRunning_TeachingRun;
|
|
public bool IsSequenceRunning_TeachingRun { get => _IsSequenceRunning_TeachingRun; set => SetProperty(ref _IsSequenceRunning_TeachingRun, value); }
|
|
|
|
private bool _IsSelectedScoutNormal;
|
|
public bool IsSelectedScoutNormal { get => _IsSelectedScoutNormal; set => SetProperty(ref _IsSelectedScoutNormal, value); }
|
|
|
|
private bool _IsGuidBeam;
|
|
public bool IsGuidBeam { get => _IsGuidBeam; set => SetProperty(ref _IsGuidBeam, value); }
|
|
private bool _IsLaserReady;
|
|
public bool IsLaserReady { get => _IsLaserReady; set => SetProperty(ref _IsLaserReady, value); }
|
|
|
|
// Object Select Type
|
|
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
|
|
{
|
|
get => _IsFrontInterlock;
|
|
set { SetProperty(ref _IsFrontInterlock, value); SaveLaserInterlock(); }
|
|
}
|
|
private bool _IsDownInterlock;
|
|
public bool IsDownInterlock
|
|
{
|
|
get => _IsDownInterlock;
|
|
set { SetProperty(ref _IsDownInterlock, value); SaveLaserInterlock(); }
|
|
}
|
|
// 로드로 인한 set 시 저장 억제(레시피 전환 시 stale 덮어쓰기 방지)
|
|
private bool _suppressInterlockSave;
|
|
private void SaveLaserInterlock()
|
|
{
|
|
if (_suppressInterlockSave) {
|
|
return;
|
|
}
|
|
RecipeLaserInterlock.Save(new RecipeLaserInterlock { Front = _IsFrontInterlock, Down = _IsDownInterlock });
|
|
}
|
|
private void LoadLaserInterlock()
|
|
{
|
|
var rl = RecipeLaserInterlock.Load();
|
|
_suppressInterlockSave = true;
|
|
try {
|
|
IsFrontInterlock = rl.Front;
|
|
IsDownInterlock = rl.Down;
|
|
} finally {
|
|
_suppressInterlockSave = false;
|
|
}
|
|
}
|
|
|
|
// ScoutNormal 행 전환 sleep 분리 (같은 행 / 행 전환)
|
|
private int _ScoutSameRowSleepMs;
|
|
public int ScoutSameRowSleepMs
|
|
{
|
|
get => _ScoutSameRowSleepMs;
|
|
set { SetProperty(ref _ScoutSameRowSleepMs, value); _SystemParameter.ScoutSameRowSleepMs = value; SaveScoutSleep(); }
|
|
}
|
|
private int _ScoutRowChangeSleepMs;
|
|
public int ScoutRowChangeSleepMs
|
|
{
|
|
get => _ScoutRowChangeSleepMs;
|
|
set { SetProperty(ref _ScoutRowChangeSleepMs, value); _SystemParameter.ScoutRowChangeSleepMs = value; SaveScoutSleep(); }
|
|
}
|
|
private void SaveScoutSleep()
|
|
{
|
|
_Aurora.SettingEntry.Save(CommonSettingIds.SystemParameter, _SystemParameter, "ScoutSleep");
|
|
}
|
|
|
|
private readonly DispatcherTimer _MonitorTimer;
|
|
|
|
private SequenceDialog _SequenceDialog;
|
|
|
|
// 레시피 화면에서 토글로 띄우는 스캐너 카메라 + 모션 창 (재사용을 위해 Hide/Show)
|
|
private WDScannerCamMotion _ScannerCamWindow;
|
|
|
|
// Scout Field 선택 팝업 (매뉴얼 화면과 동일한 창을 레시피 화면에서도 사용)
|
|
private WDSelectScoutField _SelectFieldWindow;
|
|
|
|
private WinSequenceDiagram _WinSequenceDiagramScoutNormal;
|
|
private WinSequenceDiagram _WinSequenceDiagramScoutFlyingVision;
|
|
private WinSequenceDiagram _WinSequenceDiagramTeachingRun;
|
|
private WinSequenceDiagram _WinSequenceDiagramComponentTeachingRun;
|
|
// 260826 현재 미사용 — PerformCmdLaserReadyCheck 가 DO 되읽기 방식으로 바뀌었다.
|
|
// 해당 메서드의 "260824/260826 이전 원본" 주석 블록이 이 필드를 참조하므로 원복 대비로 남겨둔다.
|
|
public bool toggleIO = true;
|
|
public UCScannerObjectTeachingControlBoxViewModel(IAuroraEnvironment aurora)
|
|
{
|
|
_Aurora = aurora;
|
|
|
|
_SystemParameter = aurora.SettingEntry[CommonSettingIds.SystemParameter] as SystemParameter;
|
|
_ScoutSameRowSleepMs = _SystemParameter.ScoutSameRowSleepMs;
|
|
_ScoutRowChangeSleepMs = _SystemParameter.ScoutRowChangeSleepMs;
|
|
|
|
_SeqScoutNormal = _Aurora.SequenceEntry[CommonSequenceIds.SeqScoutNormal];
|
|
_SeqScoutFlyingVision = _Aurora.SequenceEntry[CommonSequenceIds.SeqScoutFlyingVision];
|
|
|
|
_Scanner = _Aurora.DeviceManager[CommonDeviceIds.Scanner] as Scanner;
|
|
_Laser = _Aurora.DeviceManager[CommonDeviceIds.Laser] as Laser;
|
|
_IODevice = _Aurora.DeviceManager[CommonDeviceIds.IODevice] as IODevice;
|
|
// Trumpf 레이저인 경우에는 ComponentTeachingRun 실행
|
|
_LaserConfig = _Aurora.DeviceManager.GetDeviceConfig(CommonDeviceIds.Laser) as LaserConfig;
|
|
_SeqTeachingRun = _Aurora.SequenceEntry[CommonSequenceIds.SeqComponentTeachingRun];
|
|
|
|
InitVariable();
|
|
|
|
LoadLaserInterlock();
|
|
|
|
CMD_VisibleChanged = new DelegateCommand(VisibleChanged);
|
|
|
|
CMD_RunScoutNormal = new DelegateCommand(PerformCmdRunScoutNormal);
|
|
CMD_StopScoutNormal = new DelegateCommand(PerformCmdStopScoutNormal);
|
|
CMD_RunScoutFlyingVision = new DelegateCommand(PerformCmdRunScoutFlyingVision);
|
|
CMD_StopScoutFlyingVision = new DelegateCommand(PerformCmdStopScoutFlyingVision);
|
|
CMD_RunTeachingRun = new DelegateCommand(PerformCmdRunTeachingRun);
|
|
CMD_StopTeachingRun = new DelegateCommand(PerformCmdStopTeachingRun);
|
|
|
|
CMD_LaserGuideBeam = new DelegateCommand(PerformCmdLaserGuideBeam);
|
|
CMD_LaserReadyCheck = new DelegateCommand(PerformCmdLaserReadyCheck);
|
|
CMD_ImportObjectFromDXF = new DelegateCommand(PerformCmdImportObjectFromDXF);
|
|
CMD_ToggleScannerCamView = new DelegateCommand(PerformCmdToggleScannerCamView);
|
|
CMD_ShowSelectFieldWindow = new DelegateCommand(PerformCmdShowSelectFieldWindow, CanShowSelectFieldWindow);
|
|
|
|
_Aurora.EventObservable.AsObservable(AuroraSysEvent.SYS_SETTING_CHANGED)
|
|
.ObserveOnDispatcher()
|
|
.Subscribe(OnSettingReloaded);
|
|
|
|
// 레시피 전환 시 체크 상태를 새 레시피의 json 값으로 갱신.
|
|
// SYS_RECIPE_ACTIVATED(1-홉)에서 WorkRecipeFile은 아직 미갱신(ReloadRecipe는 2-홉 뒤) →
|
|
// Background 우선순위로 지연 실행해 ReloadRecipe(Normal) 완료 후 새 경로로 로드.
|
|
_Aurora.EventObservable.AsObservable(AuroraSysEvent.SYS_RECIPE_ACTIVATED)
|
|
.ObserveOnDispatcher()
|
|
.Subscribe(_ => Application.Current.Dispatcher.BeginInvoke(
|
|
new Action(LoadLaserInterlock), DispatcherPriority.Background));
|
|
|
|
_MonitorTimer = new DispatcherTimer();
|
|
_MonitorTimer.Tick += OnMonitoring;
|
|
_MonitorTimer.Interval = TimeSpan.FromMilliseconds(100);
|
|
|
|
}
|
|
|
|
private void InitVariable()
|
|
{
|
|
if (_SystemParameter.ScoutType == eScoutType.Normal) {
|
|
IsSelectedScoutNormal = true;
|
|
} else {
|
|
IsSelectedScoutNormal = false;
|
|
}
|
|
|
|
IsSequenceRunning_ScoutNormal = false;
|
|
IsSequenceRunning_ScoutFlyingVision = false;
|
|
IsSequenceRunning_TeachingRun = false;
|
|
|
|
IsGuidBeam = false;
|
|
IsLaserReady = false;
|
|
|
|
IsObjectSelectAll = true;
|
|
}
|
|
|
|
private void VisibleChanged(object obj)
|
|
{
|
|
bool visible = (bool)obj;
|
|
if (visible) {
|
|
LoadLaserInterlock(); // 패널 표시 시 현재 활성 레시피의 체크값 반영
|
|
_MonitorTimer.Start();
|
|
} else {
|
|
_MonitorTimer.Stop();
|
|
}
|
|
}
|
|
|
|
private void OnSettingReloaded(object obj)
|
|
{
|
|
_SystemParameter = _Aurora.SettingEntry[CommonSettingIds.SystemParameter] as SystemParameter;
|
|
if (_SystemParameter.ScoutType == eScoutType.Normal) {
|
|
IsSelectedScoutNormal = true;
|
|
} else {
|
|
IsSelectedScoutNormal = false;
|
|
}
|
|
}
|
|
|
|
private void OnMonitoring(object sender, EventArgs e)
|
|
{
|
|
// 260820: 아래 IO 블록은 미연결 시 return 하므로, PLC 지시 표시는 반드시 그 앞에서 갱신한다.
|
|
SyncPlcGuide();
|
|
|
|
// [기존 소스 보존] Laser 디바이스 기반 Pilot 상태 (DO 3=GuideLaserEnable) — 일부 Laser 미구현 시 항상 false
|
|
//if (_Laser.GetPilotLaserStatus()) {
|
|
// IsGuidBeam = true;
|
|
//} else {
|
|
// IsGuidBeam = false;
|
|
//}
|
|
try
|
|
{
|
|
// Guide Beam: DI 결선 없음 → 가이드빔 제어 DO 17 명령 비트를 그대로 표시
|
|
// (현장 가이드빔 제어선은 DO 17). IO 미연결 시 ReadDO 예외 → 아래 catch 에서 처리(앱 종료 방지)
|
|
|
|
// 시뮬 아님 + IO보드 없음 → _iOUnit == null → ReadDI에서 NRE로 꺼짐. - 현장PC 아닌 경우 개발PC에서 주석 해제 (시뮬안하고 레시피 들어갔을 때 프로그램 종료 방지)
|
|
// IsConnected()가 _iOUnit null/미연결을 체크하므로, 미연결이면 폴링 스킵(꺼짐 방지).
|
|
if (_IODevice == null || !_IODevice.IsConnected())
|
|
{
|
|
IsGuidBeam = false;
|
|
IsLaserReady = false;
|
|
|
|
return;
|
|
}
|
|
|
|
#region 260824 이전 원본 (임시레이저 채널 하드코딩)
|
|
//IsGuidBeam = _IODevice.ReadDO(17);
|
|
//IsLaserReady = _IODevice.ReadDI(11) && _IODevice.ReadDI(15); // 변경 후 (260618: IO 결선 변경 12&24 → 11&15)
|
|
#endregion
|
|
|
|
// 260824 본 레이저 단일 발진기 + enum 참조로 전환 (하드코딩 제거)
|
|
// 구조: 구 DI 11/15 = Core/Ring_Control_Status(명령 DO 9/13 의 에코).
|
|
// 본 레이저의 대응 짝은 DO Laser_REMOTE(External Start) → DI Laser_EMISSION_ON 이다.
|
|
// ★ Laser_READY(Pin21)를 쓰면 안 된다 — "Power Supply ON & No error" 라
|
|
// 전원만 들어오면 상시 High 이므로 버튼(CMD_LaserReadyCheck)에 반응하지 않고,
|
|
// :468 의 발진 차단 인터락도 무력해진다. (현장 확인 260824)
|
|
//
|
|
// 260826 협의 확정 — DI 에코(Laser_EMISSION_ON) 가 아니라 DO 명령 비트를 직접 표시한다.
|
|
// 버튼(CMD_LaserReadyCheck)이 토글하는 대상이 DO Laser_REMOTE 이므로,
|
|
// 같은 비트를 되읽어야 조작과 표시가 1:1 로 맞는다. IsGuidBeam 과 동일한 방식.
|
|
IsGuidBeam = _IODevice.ReadDO((int)ePrismaticDOAddress.Laser_GUIDE_BEAM_CONTROL);
|
|
IsLaserReady = _IODevice.ReadDO((int)ePrismaticDOAddress.Laser_REMOTE);
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.E($"[OnMonitoring] DI 읽기 실패: {ex.Message}");
|
|
IsLaserReady = false;
|
|
IsGuidBeam = false;
|
|
}
|
|
|
|
// TODO: PRIBAT_L1, PRIBAT_L2 는 Blow 의 상태 check 가 들어 있음. 장비 한정이므로 이를 check 하는 것을 modelItem 이나 setting 으로 빼서 진행되도록 함.
|
|
}
|
|
|
|
private void PerformCmdRunScoutNormal(object obj)
|
|
{
|
|
var result = MessageUtil.ShowYesNoMessageDialog("Scanner Scout", "Start Scanner Scout Normal Sequence?");
|
|
if (result == false) {
|
|
return;
|
|
}
|
|
|
|
if (VisionRecipeManager.GetFovInfos().Count == 0) {
|
|
MessageUtil.ShowWarningToast(string.Format("Please check the Vision Recipe of the currently active Recipe."));
|
|
return;
|
|
}
|
|
|
|
if (_SeqScoutNormal.RunState == SequenceRunState.Running) {
|
|
MessageUtil.ShowWarningToast(string.Format("이미 시퀀스가 Start 상태입니다.", "SeqScoutNormal"), Log);
|
|
return;
|
|
}
|
|
|
|
var scoutSeqParams = new ScoutSeqParams(VisionRecipeManager.GetFovInfos()[0], eDisplayType.Machine_B);
|
|
|
|
#region 260807 이전 원본 (주석 처리 — 원복 시 이 블록을 살리고 아래 260807 블록을 제거한다)
|
|
//var param = new SeqScoutNormal_InitParam() {
|
|
// IsRealTimeScout = false,
|
|
// ScoutSeqParams = scoutSeqParams,
|
|
// // [추가 2026-06-19] 현재 표시 중인 페이지의 스카우트 필드를 사용하도록 명시 주입
|
|
// PageIndex = CommonHelper.GetMainPage().GetCurrentPageIndex(),
|
|
//};
|
|
#endregion
|
|
|
|
#region 260807: 레시피/페이지 번호 반영 SaveTag 변경 — 파일명 {기존}_normal_{recipeId}_{pageNo}.PNG
|
|
int pageIndex = CommonHelper.GetMainPage().GetCurrentPageIndex(); // 0-base (콤보박스 SelectedIndex)
|
|
int recipeId = _Aurora.RecipeManager.ActivatedRecipe()?.Id ?? 0; // 레시피 미활성 시 0
|
|
var param = new SeqScoutNormal_InitParam() {
|
|
IsRealTimeScout = false,
|
|
ScoutSeqParams = scoutSeqParams,
|
|
// [추가 2026-06-19] 현재 표시 중인 페이지의 스카우트 필드를 사용하도록 명시 주입
|
|
PageIndex = pageIndex,
|
|
// 파일명 접미어: normal_{레시피ID}_{화면 페이지 번호(1-base)} → AutoRun 의 auto_{recipe}_{page}_pre/post 와 숫자 정합
|
|
SaveTag = $"normal_{recipeId}_{pageIndex + 1}",
|
|
};
|
|
#endregion
|
|
|
|
PopupSequenceDiagram(ref _WinSequenceDiagramScoutNormal, CommonSequenceIds.SeqScoutNormal, param);
|
|
|
|
IsSequenceRunning_ScoutNormal = true;
|
|
_SeqScoutNormal.Start(param, OnCompleteScoutNormal);
|
|
|
|
}
|
|
|
|
private void OnCompleteScoutNormal(SequenceActionResult res)
|
|
{
|
|
if (res != null) {
|
|
if (res.Param is SeqScoutNormal_OutParam result && result.Success == true) {
|
|
IsSequenceRunning_ScoutNormal = false;
|
|
MessageUtil.ShowSuccessToast(string.Format("Success SeqScoutNormal"));
|
|
|
|
CloseSequenceDiagram(_WinSequenceDiagramScoutNormal);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (_Laser.GetLaserReady()) {
|
|
_Laser.LaserOff();
|
|
}
|
|
|
|
IsSequenceRunning_ScoutNormal = false;
|
|
MessageUtil.ShowWarningToast(string.Format("Fail SeqScoutNormal"));
|
|
}
|
|
|
|
private void PerformCmdStopScoutNormal(object obj)
|
|
{
|
|
if (_SeqScoutNormal.RunState == SequenceRunState.Idle) {
|
|
return;
|
|
}
|
|
|
|
IsSequenceRunning_ScoutNormal = false;
|
|
_SeqScoutNormal.Stop();
|
|
}
|
|
|
|
private void PerformCmdRunScoutFlyingVision(object obj)
|
|
{
|
|
var result = MessageUtil.ShowYesNoMessageDialog("Flying Vision", "Start Scanner Scout Flying Vision Sequence?");
|
|
if (result == false) {
|
|
return;
|
|
}
|
|
|
|
if (VisionRecipeManager.GetFovInfos().Count == 0) {
|
|
MessageUtil.ShowWarningToast(string.Format("Please check the Vision Recipe of the currently active Recipe."));
|
|
return;
|
|
}
|
|
|
|
if (_SeqScoutFlyingVision.RunState == SequenceRunState.Running) {
|
|
MessageUtil.ShowWarningToast(string.Format("이미 시퀀스가 Start 상태입니다.", "SeqScoutFlyingVision"), Log);
|
|
return;
|
|
}
|
|
|
|
var scoutSeqParams = new ScoutSeqParams(VisionRecipeManager.GetFovInfos()[0], eDisplayType.Machine_B);
|
|
var param = new SeqScoutFlyingVision_InitParam() {
|
|
IsRealTimeScout = false,
|
|
ScoutSeqParams = scoutSeqParams,
|
|
SequenceAutoRunCount = 0,
|
|
WeldingScannerListNo = eListNo.LIST2,
|
|
};
|
|
|
|
PopupSequenceDiagram(ref _WinSequenceDiagramScoutFlyingVision, CommonSequenceIds.SeqScoutFlyingVision, param);
|
|
|
|
IsSequenceRunning_ScoutFlyingVision = true;
|
|
_SeqScoutFlyingVision.Start(param, OnCompleteScoutFlyingVision);
|
|
|
|
}
|
|
|
|
private void OnCompleteScoutFlyingVision(SequenceActionResult res)
|
|
{
|
|
if (res != null) {
|
|
if (res.Param is SeqScoutFlyingVision_OutParam result && result.Success == true) {
|
|
IsSequenceRunning_ScoutFlyingVision = false;
|
|
MessageUtil.ShowSuccessToast(string.Format("Success SeqScoutFlyingVision"));
|
|
|
|
CloseSequenceDiagram(_WinSequenceDiagramScoutFlyingVision);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (_Laser.GetLaserReady()) {
|
|
_Laser.LaserOff();
|
|
}
|
|
|
|
IsSequenceRunning_ScoutFlyingVision = false;
|
|
MessageUtil.ShowWarningToast(string.Format("Fail SeqScoutFlyingVision"));
|
|
}
|
|
|
|
private void PerformCmdStopScoutFlyingVision(object obj)
|
|
{
|
|
if (_SeqScoutFlyingVision.RunState == SequenceRunState.Idle) {
|
|
return;
|
|
}
|
|
|
|
IsSequenceRunning_ScoutFlyingVision = false;
|
|
_SeqScoutFlyingVision.Stop();
|
|
}
|
|
|
|
private void PerformCmdRunTeachingRun(object obj)
|
|
{
|
|
var result = MessageUtil.ShowYesNoMessageDialog("Teaching Run", "Start TeachingRun Sequence?");
|
|
if (result == false) {
|
|
return;
|
|
}
|
|
|
|
if (IsLaserReady == false) {
|
|
MessageUtil.ShowWarningToast(string.Format("Please check Laser Ready."));
|
|
return;
|
|
}
|
|
|
|
// TODO: PRIBAT_L1, PRIBAT_L2 에서 Suction 과 CLAMP 상태 등에 따라 Laser 가 나가지 않도록 해야 함.
|
|
// TODO: 이 부분은, Delegate 를 사용해서, Interlock 을 각 장비별로 설정할 수 있도록 해주는게 좋겠음.
|
|
|
|
if (_SeqTeachingRun.RunState == SequenceRunState.Running) {
|
|
MessageUtil.ShowWarningToast(string.Format("이미 시퀀스가 Start 상태입니다.", "SeqTeachingRun"), Log);
|
|
return;
|
|
}
|
|
|
|
// TODO: NeedSetLaserTiming 는 트럼프 레이저 를 사용할 때, UploadWeldingList Sequence 에서 LaserTiming Set 을 해야 할때 사용함.
|
|
// TODO: SMW 와 LHS 는 트럼프 레이저를 사용하지 않으므로 false, PRIBAT_L1 과 PRIBAT_L2 할때 LaserType 을 읽어서 설정하는 것으로 추가하자
|
|
|
|
var currentModelInfo = EquipmentSpecificModelUtil.GetCurrentRecipeEquipmentSpecificModel();
|
|
if (currentModelInfo.equipmentSpecificModel == null) {
|
|
MessageUtil.ShowWarningToast(string.Format("선택된 model 이 없습니다.", "SeqTeachingRun"));
|
|
return;
|
|
}
|
|
|
|
var intervalTimeBlowOn = 0;
|
|
var intervalTimeBlowOff = 0;
|
|
|
|
if (currentModelInfo.equipmentSpecificModel.UseBlow) {
|
|
intervalTimeBlowOn = currentModelInfo.equipmentSpecificModel.IntervalTimeBlowOn;
|
|
intervalTimeBlowOff = currentModelInfo.equipmentSpecificModel.IntervalTimeBlowOff;
|
|
}
|
|
|
|
var param = new SeqTeachingRun_InitParam() {
|
|
IsObjectSelectAll = IsObjectSelectAll,
|
|
|
|
//UseStream = _EquipmentInfo.UseStream,
|
|
UseStream = currentModelInfo.equipmentSpecificModel.UseStream,
|
|
|
|
UseBlow = currentModelInfo.equipmentSpecificModel.UseBlow,
|
|
IntervalTimeBlowOn = intervalTimeBlowOn,
|
|
IntervalTimeBlowOff = intervalTimeBlowOff,
|
|
|
|
NeedSetLaserTiming = false,
|
|
};
|
|
|
|
//PopupSequenceDiagram(ref _WinSequenceDiagramTeachingRun, CommonSequenceIds.SeqComponentTeachingRun, param);
|
|
|
|
IsSequenceRunning_TeachingRun = true;
|
|
_SeqTeachingRun.Start(param, OnCompleteTeachingRun);
|
|
|
|
}
|
|
|
|
private void OnCompleteTeachingRun(SequenceActionResult res)
|
|
{
|
|
|
|
//if (res != null) {
|
|
// if (res.Param is SeqTeachingRun_OutParam result && result.Success == true) {
|
|
// IsSequenceRunning_TeachingRun = false;
|
|
// MessageUtil.ShowSuccessToast(string.Format("Success SeqTeachingRun"));
|
|
|
|
// CloseSequenceDiagram(_WinSequenceDiagramTeachingRun);
|
|
// return;
|
|
// }
|
|
//}
|
|
|
|
if (_Laser.GetLaserReady()) {
|
|
_Laser.LaserOff();
|
|
}
|
|
|
|
IsSequenceRunning_TeachingRun = false;
|
|
//MessageUtil.ShowWarningToast(string.Format("Fail SeqTeachingRun"));
|
|
}
|
|
|
|
private void PerformCmdStopTeachingRun(object obj)
|
|
{
|
|
IsSequenceRunning_TeachingRun = false;
|
|
if (_SeqTeachingRun.RunState == SequenceRunState.Idle) {
|
|
return;
|
|
}
|
|
|
|
_SeqTeachingRun.Stop();
|
|
}
|
|
|
|
|
|
private void PopupSequenceDiagram(ref WinSequenceDiagram winSequenceDiagram, string seqId, object param)
|
|
{
|
|
if (winSequenceDiagram == null) {
|
|
winSequenceDiagram = new WinSequenceDiagram(seqId);
|
|
winSequenceDiagram.Closing += ClosingEvent;
|
|
winSequenceDiagram.SetParam(new SeqDrawerParam() {
|
|
SequenceId = seqId,
|
|
DiagramType = eDiagramType.Tree,
|
|
LineType = eLineType.Straight,
|
|
IsShowSubSequence = false,
|
|
VisibleKeys = new List<string>()
|
|
});
|
|
|
|
winSequenceDiagram.Owner = Application.Current.MainWindow;
|
|
winSequenceDiagram.Width = 600;
|
|
winSequenceDiagram.SequenceParam = param;
|
|
winSequenceDiagram.Show();
|
|
} else {
|
|
winSequenceDiagram.SequenceParam = param;
|
|
winSequenceDiagram.WindowState = WindowState.Normal;
|
|
winSequenceDiagram.Show();
|
|
}
|
|
}
|
|
private void CloseSequenceDiagram(WinSequenceDiagram winSequenceDiagram)
|
|
{
|
|
Application.Current.Dispatcher.Invoke(() => {
|
|
if (winSequenceDiagram != null) {
|
|
winSequenceDiagram.Close();
|
|
}
|
|
});
|
|
}
|
|
private void ClosingEvent(object sender, CancelEventArgs e)
|
|
{
|
|
e.Cancel = true;
|
|
(sender as Window).Hide();
|
|
}
|
|
|
|
private void PerformCmdLaserGuideBeam(object obj)
|
|
{
|
|
// Guide Beam DO 토글 — 모니터링(OnMonitoring)도 같은 DO 를 읽어 표시
|
|
// 260824: DO 17(EX_Red_Control) → ePrismaticDOAddress.Laser_GUIDE_BEAM_CONTROL 로 변경됨
|
|
// IO 미연결 가드는 현장PC에서 동작 안 해 주석 (개발PC에서 주석 해제)
|
|
//if (_IODevice == null || !_IODevice.IsConnected()) {
|
|
// return;
|
|
//}
|
|
#region 260824 이전 원본 (임시레이저 DO 17 하드코딩)
|
|
//bool cur = _IODevice.ReadDO(17);
|
|
//_IODevice._iOUnit.WriteDoBit(17, !cur);
|
|
#endregion
|
|
|
|
// 260824 본 레이저 가이드빔 DO 로 전환 (EX_Red_Control 17 → Laser_GUIDE_BEAM_CONTROL)
|
|
bool cur = _IODevice.ReadDO((int)ePrismaticDOAddress.Laser_GUIDE_BEAM_CONTROL);
|
|
_IODevice._iOUnit.WriteDoBit((int)ePrismaticDOAddress.Laser_GUIDE_BEAM_CONTROL, !cur);
|
|
|
|
// [기존 소스 보존] Laser 디바이스 기반 Pilot On/Off 토글 (DO 3=GuideLaserEnable)
|
|
//if (!_Laser.GetPilotLaserSetBitStatus()) {
|
|
// _Laser.PilotLaserOn();
|
|
//} else {
|
|
// _Laser.PilotLaserOff();
|
|
//}
|
|
}
|
|
|
|
private void PerformCmdLaserReadyCheck(object obj)
|
|
{
|
|
// 추후 Write bit 파라미터화 해야함
|
|
#region 260824 이전 원본 (임시레이저 Core/Ring_Control 2발 하드코딩)
|
|
//if(toggleIO)
|
|
//{
|
|
// _IODevice._iOUnit.WriteDoBit(9, true);
|
|
// _IODevice._iOUnit.WriteDoBit(13, true);
|
|
// toggleIO = false;
|
|
//}
|
|
//else
|
|
//{
|
|
// _IODevice._iOUnit.WriteDoBit(9, false);
|
|
// _IODevice._iOUnit.WriteDoBit(13, false);
|
|
// toggleIO = true;
|
|
//}
|
|
#endregion
|
|
|
|
#region 260826 이전 원본 (toggleIO 필드 토글 — 원복 시 이 블록을 살리고 아래 2줄을 제거)
|
|
//// 260824 본 레이저 단일 발진기 — 제어권(REMOTE) 1발 토글
|
|
//// ※ AC_KEY(DO 8)는 전원 투입 계층이라 여기서 건드리지 않는다(반 하드와이어 여부 미확정, Q3).
|
|
//if (toggleIO)
|
|
//{
|
|
// _IODevice._iOUnit.WriteDoBit((int)ePrismaticDOAddress.Laser_REMOTE, true);
|
|
// toggleIO = false;
|
|
//}
|
|
//else
|
|
//{
|
|
// _IODevice._iOUnit.WriteDoBit((int)ePrismaticDOAddress.Laser_REMOTE, false);
|
|
// toggleIO = true;
|
|
//}
|
|
#endregion
|
|
|
|
// 260826 현재 DO 를 읽어 반전하는 방식으로 전환 (바로 위 PerformCmdLaserGuideBeam :607-608 과 동일 패턴).
|
|
// 기존 toggleIO 는 "다음에 쓸 값"을 VM 이 기억하는 방식이라 실제 DO 와 어긋났다.
|
|
// SeqAutoRunPTOP 가 같은 DO 를 쓰면서 toggleIO 를 갱신하지 않기 때문 —
|
|
// :1220 용접시작 true / :1243 용접종료 false / :1701 정지 false
|
|
// 어긋나면 버튼을 눌러도 같은 값을 다시 써서 아무 변화가 없다(= 두 번 눌러야 반응. 현장 확인 260826).
|
|
// 현재 DO 를 읽어 반전하면 누가 중간에 건드려도 1회 입력에 TRUE/FALSE 가 정확히 기록되고,
|
|
// 표시(OnMonitoring 의 IsLaserReady = ReadDO(Laser_REMOTE))와도 1:1 로 맞는다.
|
|
// ※ AC_KEY(DO 8)는 전원 투입 계층이라 여기서 건드리지 않는다(반 하드와이어 여부 미확정, Q3).
|
|
bool curRemote = _IODevice.ReadDO((int)ePrismaticDOAddress.Laser_REMOTE);
|
|
_IODevice._iOUnit.WriteDoBit((int)ePrismaticDOAddress.Laser_REMOTE, !curRemote);
|
|
}
|
|
|
|
private void PerformCmdToggleScannerCamView(object obj)
|
|
{
|
|
if (_ScannerCamWindow == null) {
|
|
_ScannerCamWindow = new WDScannerCamMotion(_Aurora, CommonDeviceIds.Scanner) {
|
|
Owner = Application.Current.MainWindow,
|
|
};
|
|
}
|
|
|
|
if (_ScannerCamWindow.IsVisible) {
|
|
_ScannerCamWindow.Hide();
|
|
} else {
|
|
_ScannerCamWindow.Show();
|
|
_ScannerCamWindow.Activate();
|
|
}
|
|
}
|
|
|
|
private bool CanShowSelectFieldWindow(object o) => _SelectFieldWindow == null;
|
|
|
|
private void PerformCmdShowSelectFieldWindow(object o)
|
|
{
|
|
// 매뉴얼 화면과 동일한 Scout Field 선택 팝업을 띄움. 저장은 팝업 VM(PerformCmdSave)에서 자체 처리.
|
|
try {
|
|
Application.Current.Dispatcher.Invoke(
|
|
new Action(() => {
|
|
if (_SelectFieldWindow != null) {
|
|
_SelectFieldWindow.Close();
|
|
_SelectFieldWindow = null;
|
|
}
|
|
|
|
_SelectFieldWindow = new WDSelectScoutField(_Aurora) {
|
|
Owner = Application.Current.MainWindow
|
|
};
|
|
_SelectFieldWindow.Closed += (sender, _) => { _SelectFieldWindow = null; };
|
|
_SelectFieldWindow.Show();
|
|
}));
|
|
}
|
|
catch (Exception ex) {
|
|
Log.E($"[PerformCmdShowSelectFieldWindow] 실패: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void PerformCmdImportObjectFromDXF(object obj)
|
|
{
|
|
System.Windows.Forms.FileDialog dlg = new System.Windows.Forms.OpenFileDialog();
|
|
dlg.InitialDirectory = @"D:\share\jungwoo.jin\모바일글라스도면 외 (3) 파일_20241028";
|
|
dlg.DefaultExt = "dxf";
|
|
dlg.Filter = "DXF 도면 파일 (*.dxf)|*.dxf";
|
|
dlg.Title = "DXF 도면 열기";
|
|
|
|
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
|
|
CommonHelper.GetMainPage().DxfParser(dlg.FileName);
|
|
}
|
|
}
|
|
}
|
|
}
|