diff --git a/WeldingComponents/WeldingCommon/EquipDefCommon.cs b/WeldingComponents/WeldingCommon/EquipDefCommon.cs index 816c249..1f45532 100644 --- a/WeldingComponents/WeldingCommon/EquipDefCommon.cs +++ b/WeldingComponents/WeldingCommon/EquipDefCommon.cs @@ -144,6 +144,13 @@ namespace WeldingCommon public const string EVENT_CHANGE_DOOR_LOCK = "EventChangeDoorLock"; public const string EVENT_CHANGE_RECIPE_INFO = "EventChangeRecipeInfo"; public const string EVENT_AUTO_RUN_UPDATE_PAGENUM = "EventAutoRunUpdatePageNum"; + + // 레시피 화면(publish) → 매뉴얼 스캐너 VM(subscribe)로 스캐너 모션 요청을 전달한다. + // Param: WeldingCommon.Views.ScannerCommandMoveParam + // 주의: 구독자(UCScannerMotionViewModel, execute 모드)는 RegisterViews에서 기동 시 1회 생성되어 + // 프로세스 내내 생존하는 전제(EquipmentSetup.RegisterViews)에 의존한다. View 생성을 + // 지연 생성으로 바꾸면 구독자가 없어 본 이벤트가 무시될 수 있으므로 함께 점검할 것. + public const string EVENT_SCANNER_COMMAND_MOVE = "EventScannerCommandMove"; } public class CommonSequenceIds diff --git a/WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerMotionViewModel.cs b/WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerMotionViewModel.cs index 7ef6295..155a7ba 100644 --- a/WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerMotionViewModel.cs +++ b/WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerMotionViewModel.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Reactive.Linq; using System.Windows.Input; using System.Windows.Threading; @@ -34,6 +35,29 @@ namespace WeldingCommon.Views REL }; + public enum eScannerMoveKind + { + CommandPos, + Jog, + Home + } + + /// + /// 레시피 화면(publish 모드)에서 매뉴얼 스캐너 VM(execute 모드)으로 보내는 모션 요청 페이로드. + /// 매뉴얼 VM의 저장 state가 아니라 이 값으로 동작하도록 모든 입력값을 담는다. + /// + public class ScannerCommandMoveParam + { + public eScannerMoveKind Kind; + public eAxisType Axis; + public eJogDirection Direction; + public double CommandPosX; + public double CommandPosY; + public double StepValue; + public eMotionMode Mode; + public double VelocityRate; + } + public class UCScannerMotionViewModel : NotifyPropertyChangedBase { private readonly Logger Log = new Logger("UCScannerMotionViewModel"); @@ -80,12 +104,17 @@ namespace WeldingCommon.Views private double _VelocityRate; private eMotionMode _MotionMode; + // publish 모드: UI(레시피)는 직접 디바이스를 제어하지 않고, 입력값을 이벤트로 발행한다. + // execute 모드(기본, 매뉴얼): 이벤트를 구독해 실제 스캐너를 움직인다. + private readonly bool _PublishMode; + private readonly DispatcherTimer _MonitorTimer; // PAM, RUW 처럼 2개 이상의 scanner 를 사용하는 경우가 있어, scannerId 를 받는 형태로 생성 - public UCScannerMotionViewModel(IAuroraEnvironment aurora, string scannerId) + public UCScannerMotionViewModel(IAuroraEnvironment aurora, string scannerId, bool publishMode = false) { _Aurora = aurora; + _PublishMode = publishMode; _Scanner = _Aurora.DeviceManager[scannerId] as Scanner; _Laser = _Aurora.DeviceManager[CommonDeviceIds.Laser] as Laser; @@ -99,11 +128,52 @@ namespace WeldingCommon.Views CMD_JogMove = new DelegateCommand(PerformCmdJogMove); CMD_CommandPosMove = new DelegateCommand(PerformCmdCommandPosMove); + // execute 모드만 원격 모션 요청을 구독한다(중복 실행 방지). UI 스레드에서 처리. + if (!_PublishMode) { + _Aurora.EventObservable.AsObservable(CommonEventIds.EVENT_SCANNER_COMMAND_MOVE) + .ObserveOnDispatcher() + .Subscribe(OnRemoteMotionRequested); + } + _MonitorTimer = new DispatcherTimer(); _MonitorTimer.Tick += OnMonitoring; _MonitorTimer.Interval = TimeSpan.FromMilliseconds(100); } + private void PublishMotion(ScannerCommandMoveParam param) + { + _Aurora.EventPublisher.Publish( + new EventMessage("UCScannerMotionViewModel.Publish", CommonEventIds.EVENT_SCANNER_COMMAND_MOVE, param)); + } + + private void OnRemoteMotionRequested(EventMessage msg) + { + if (msg == null || !(msg.Param is ScannerCommandMoveParam p)) { + return; + } + + switch (p.Kind) { + case eScannerMoveKind.CommandPos: + MoveCommandXYPos(p.Axis, p.CommandPosX, p.CommandPosY, p.Mode, p.VelocityRate); + break; + + case eScannerMoveKind.Jog: + if (IsMainSequenceRunning()) return; + MoveJogXYPos(p.Axis, p.Direction, p.StepValue, p.VelocityRate); + break; + + case eScannerMoveKind.Home: + if (IsMainSequenceRunning()) return; + MoveJogHome(p.VelocityRate); + break; + } + } + + private bool IsMainSequenceRunning() + { + return _Aurora.SequenceEntry[CommonSequenceIds.SeqAutoRun].RunState == SequenceRunState.Running; + } + private void InitVariable() { _VelocityRate = 1; @@ -190,49 +260,56 @@ namespace WeldingCommon.Views { // TODO: Left, Right, Up, Down 은 장비 Scanner 의 방향에 따라 달라질 수 있다 // 양산중에는 동작 하면 안됨 - var MainSequence = _Aurora.SequenceEntry[CommonSequenceIds.SeqAutoRun].RunState; - if(MainSequence == SequenceRunState.Running) + if (IsMainSequenceRunning()) return; + eAxisType axis; + eJogDirection direction; switch ((string)commandParameter) { - case "Left": { - MoveJogXYPos(eAxisType.AXIS_X, eJogDirection.MINUS); + case "Left": axis = eAxisType.AXIS_X; direction = eJogDirection.MINUS; break; + case "Right": axis = eAxisType.AXIS_X; direction = eJogDirection.PLUS; break; + case "Up": axis = eAxisType.AXIS_Y; direction = eJogDirection.PLUS; break; + case "Down": axis = eAxisType.AXIS_Y; direction = eJogDirection.MINUS; break; + case "Home": + if (_PublishMode) { + PublishMotion(new ScannerCommandMoveParam { + Kind = eScannerMoveKind.Home, + VelocityRate = _VelocityRate, + }); + } else { + MoveJogHome(_VelocityRate); } - break; + return; - case "Right": { - MoveJogXYPos(eAxisType.AXIS_X, eJogDirection.PLUS); - } - break; - - case "Up": { - MoveJogXYPos(eAxisType.AXIS_Y, eJogDirection.PLUS); - } - break; - - case "Down": { - MoveJogXYPos(eAxisType.AXIS_Y, eJogDirection.MINUS); - } - break; - - case "Home": { - MoveJogHome(); - } - break; + default: + return; } + + if (_PublishMode) { + PublishMotion(new ScannerCommandMoveParam { + Kind = eScannerMoveKind.Jog, + Axis = axis, + Direction = direction, + StepValue = StepValue, + VelocityRate = _VelocityRate, + }); + return; + } + + MoveJogXYPos(axis, direction, StepValue, _VelocityRate); } - private void MoveJogXYPos(eAxisType axisType, eJogDirection direction) + private void MoveJogXYPos(eAxisType axisType, eJogDirection direction, double stepValue, double velocityRate) { double xPos = 0.0; double yPos = 0.0; switch (axisType) { case eAxisType.AXIS_X: { if (direction == eJogDirection.MINUS) { - xPos = StepValue * -1; + xPos = stepValue * -1; yPos = 0; } else if (direction == eJogDirection.PLUS) { - xPos = StepValue; + xPos = stepValue; yPos = 0; } } @@ -241,10 +318,10 @@ namespace WeldingCommon.Views case eAxisType.AXIS_Y: { if (direction == eJogDirection.MINUS) { xPos = 0; - yPos = StepValue * -1; + yPos = stepValue * -1; } else if (direction == eJogDirection.PLUS) { xPos = 0; - yPos = StepValue; + yPos = stepValue; } } break; @@ -259,7 +336,7 @@ namespace WeldingCommon.Views return; } - double finalVelocity = _ScannerConfig.JumpSpeed * _VelocityRate; + double finalVelocity = _ScannerConfig.JumpSpeed * velocityRate; _Scanner.SetJumpSpeedCtrl(finalVelocity); _Scanner.JumpRel(xPos, yPos); @@ -267,9 +344,9 @@ namespace WeldingCommon.Views _Scanner.SetJumpSpeedCtrl(_ScannerConfig.JumpSpeed); } - private void MoveJogHome() + private void MoveJogHome(double velocityRate) { - double finalVelocity = _ScannerConfig.JumpSpeed * _VelocityRate; + double finalVelocity = _ScannerConfig.JumpSpeed * velocityRate; _Scanner.SetJumpSpeedCtrl(finalVelocity); _Scanner.JumpAbs(0, 0); @@ -279,28 +356,37 @@ namespace WeldingCommon.Views private void PerformCmdCommandPosMove(object commandParameter) { + eAxisType axis; switch ((string)commandParameter) { - case "X": { - MoveCommandXYPos(eAxisType.AXIS_X); - } - break; - - case "Y": { - MoveCommandXYPos(eAxisType.AXIS_Y); - } - break; + case "X": axis = eAxisType.AXIS_X; break; + case "Y": axis = eAxisType.AXIS_Y; break; + default: return; } + + if (_PublishMode) { + PublishMotion(new ScannerCommandMoveParam { + Kind = eScannerMoveKind.CommandPos, + Axis = axis, + CommandPosX = CommandPosX, + CommandPosY = CommandPosY, + Mode = _MotionMode, + VelocityRate = _VelocityRate, + }); + return; + } + + MoveCommandXYPos(axis, CommandPosX, CommandPosY, _MotionMode, _VelocityRate); } - private void MoveCommandXYPos(eAxisType axisType) + private void MoveCommandXYPos(eAxisType axisType, double commandPosX, double commandPosY, eMotionMode motionMode, double velocityRate) { double xPos = 0.0; double yPos = 0.0; switch (axisType) { case eAxisType.AXIS_X: { - xPos = CommandPosX; + xPos = commandPosX; - if (_MotionMode == eMotionMode.ABS) { + if (motionMode == eMotionMode.ABS) { yPos = _Scanner.GetCommandPosY(); } else { yPos = 0.0; @@ -309,13 +395,13 @@ namespace WeldingCommon.Views break; case eAxisType.AXIS_Y: { - if (_MotionMode == eMotionMode.ABS) { + if (motionMode == eMotionMode.ABS) { xPos = _Scanner.GetCommandPosX(); } else { xPos = 0.0; } - yPos = CommandPosY; + yPos = commandPosY; } break; @@ -324,7 +410,7 @@ namespace WeldingCommon.Views } // 현재 Curruent Pos 기준으로, 입력방향으로 입력값이 Limit 를 넘어 서는지 확인 - if (_MotionMode == eMotionMode.ABS) { + if (motionMode == eMotionMode.ABS) { if (CheckOverLimitPos(xPos, yPos)) { Log.E(string.Format("움직이려는 범위가 스캐너 범위를 초과하였습니다.")); return; @@ -336,10 +422,10 @@ namespace WeldingCommon.Views } } - double finalVelocity = _ScannerConfig.JumpSpeed * _VelocityRate; + double finalVelocity = _ScannerConfig.JumpSpeed * velocityRate; _Scanner.SetJumpSpeedCtrl(finalVelocity); - if (_MotionMode == eMotionMode.ABS) { + if (motionMode == eMotionMode.ABS) { _Scanner.JumpAbs(xPos, yPos); } else { _Scanner.JumpRel(xPos, yPos); diff --git a/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBox.xaml b/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBox.xaml index 0847463..ec55db9 100644 --- a/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBox.xaml +++ b/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBox.xaml @@ -30,6 +30,7 @@ + @@ -153,7 +154,14 @@ - + + +