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 @@
-
+
+
+