diff --git a/Doc/ToNDH/.~lock.개발사항 전달.pptx# b/Doc/ToNDH/.~lock.개발사항 전달.pptx#
new file mode 100644
index 0000000..3147cc7
--- /dev/null
+++ b/Doc/ToNDH/.~lock.개발사항 전달.pptx#
@@ -0,0 +1 @@
+,DESKTOP-CN4O63O/User,DESKTOP-CN4O63O,06.04.2026 14:05,file:///C:/Users/User/AppData/Roaming/LibreOffice/4;
\ No newline at end of file
diff --git a/Doc/ToNDH/MOTF_구현가이드_26.04.06.md b/Doc/ToNDH/MOTF_구현가이드_26.04.06.md
new file mode 100644
index 0000000..8d6619c
--- /dev/null
+++ b/Doc/ToNDH/MOTF_구현가이드_26.04.06.md
@@ -0,0 +1,156 @@
+# ILSOS PrismaticScanner - MOTF 구현 가이드
+> 작성일: 2026-04-06 | 작성자: NDY
+
+---
+
+## 목차
+1. [MOTF 개념](#1-motf-개념)
+2. [변경된 파일 목록](#2-변경된-파일-목록)
+3. [흐름 정리](#3-흐름-정리)
+4. [변수 맵 수신 후 작업](#4-변수-맵-수신-후-작업)
+5. [TODO 체크리스트](#5-todo-체크리스트)
+
+---
+
+## 1. MOTF 개념
+
+```
+MOTF (Mark On The Fly)
+= Stage X(ACS)가 이동하는 동안 갈바노 스캐너가 레이저 마킹
+
+트리거: Stage X 엔코더 위치 기반
+ → WaitForEncoderX 조건 도달 시 갈바노 마킹 시작
+```
+
+### 기존 방식 vs MOTF 방식
+
+| 항목 | 기존 (갈바노 단독) | MOTF |
+|---|---|---|
+| Stage X | 정지 상태 | 이동 중 |
+| 마킹 트리거 | 즉시 실행 | 엔코더 위치 트리거 |
+| 처리량 | 낮음 | 높음 |
+
+---
+
+## 2. 변경된 파일 목록
+
+### WeldingCommon/Devices/ACS/
+- `ScannerACS.cs` - Scanner 전용 ACS 디바이스 (Gun과 독립)
+- `ScannerACS.Parameter.cs` - ACS 변수명 상수 **(TODO: 변수 맵 교체 필요)**
+- `MotionVariable.cs` - 모터 상태값
+
+### WeldingCommon/Views/
+- `UCScannerMotionViewModel.cs` - Stage X 조그 UI 연동
+- `UCScannerMotion.xaml` - Stage X Motion 섹션 추가
+
+### ILSOS_PrismaticScanner/
+- `EquipmentSetup.cs` - ScannerACS + 모터 등록
+- `EquipDef.cs` - MotorIds 추가
+- `Sequences/SeqComponentTeachingRun.cs` - MOTF 자리 확보 (주석)
+
+---
+
+## 3. 흐름 정리
+
+### SeqComponentTeachingRun 흐름
+
+```
+CheckEndList
+ ↓
+UpdateComponentWeldingParameters (레이저 파라미터 설정)
+ ↓
+SetUploadWeldingList_SeqParams
+ ↓
+SeqUploadWeldingList (Scanner List 채우기 + MOTF ON/OFF)
+ ↓ Scanner.Config.UseMarkingOnTheFly = true 이면
+ ↓ SetFlyX() + WaitForEncoderX() 자동 실행
+GetUploadWeldingList_SeqResult
+ ↓
+DoComponentWelding
+ ├── _Scanner.ExecuteList() (Scanner List 실행)
+ ├── _ACS?.MOTFStart() ← TODO: 변수 맵 수신 후 주석 해제
+ ├── _Scanner.WaitForListTaskCompleted()
+ └── CheckMOTFComp() ← TODO: 변수 맵 수신 후 주석 해제
+ ↓
+CheckEndList (반복)
+```
+
+---
+
+## 4. 변수 맵 수신 후 작업
+
+### 4-1. ScannerACS.Parameter.cs - 변수명 교체
+
+```csharp
+// 현재 placeholder → 실제 변수명으로 교체
+const string iMOTFStart = "iPCset_SCANNER_MOTF_START"; // TODO
+const string iMOTFComplete = "iPCget_SCANNER_MOTF_COMP"; // TODO
+const string rMOTFVelocity = "rPCset_SCANNER_MOTF_VEL"; // TODO
+const string rMOTFAcc = "rPCset_SCANNER_MOTF_ACC"; // TODO
+const string iAllAxisHomeComplete = "iPCget_ALLAXIS_HOMECOMP"; // TODO
+const string AlarmReset = "iAlarmreset"; // TODO
+const string HeavyAlarm = "iAlarm"; // TODO
+const string LightAlarm = "iLightAlarm"; // TODO
+
+private enum eBuffer
+{
+ All_Axis_Home = 0, // TODO: 실제 버퍼 번호로 교체
+ MOTF_Start = 11, // TODO: 실제 버퍼 번호로 교체
+}
+```
+
+### 4-2. EquipDef.cs - MotorIds 축 번호 검증
+
+```csharp
+public enum eMotorIdx
+{
+ AxisX2 = 1, // TODO: 실제 ACS 채널 번호 확인
+ AxisY1 = 3, // TODO: 실제 ACS 채널 번호 확인
+ AxisY2 = 4, // TODO: 실제 ACS 채널 번호 확인
+ AxisZ = 5, // TODO: 실제 ACS 채널 번호 확인
+}
+```
+
+### 4-3. SeqComponentTeachingRun.cs - 주석 해제 3곳
+
+```csharp
+// [1] CheckDeviceReady() - ACS 활성화 확인 추가
+// DeviceIds.ACS ← 주석 해제
+
+// [2] DoComponentWelding() - Stage X 이동 시작
+// _ACS?.MOTFStart(); ← 주석 해제
+
+// [3] DoComponentWelding() - MOTF 완료 대기
+// while (!_ACS.CheckMOTFComp()) { Thread.Sleep(10); } ← 주석 해제
+```
+
+### 4-4. Scanner Config 설정 확인
+
+```
+Scanner.Config.UseMarkingOnTheFly = true ← 반드시 확인
+Scanner.Config.OnTheFlyCoordinate = eOntheFlyCoordinate.XY ← Linear Track 방식
+Scanner.Config.MarkingOnTheFlyEncoderResolutionX ← 엔코더 분해능
+Scanner.Config.MarkingStartEncoderPositionX ← 마킹 시작 엔코더 위치
+```
+
+---
+
+## 5. TODO 체크리스트
+
+### 이정인 선임 변수 맵 수신 전 (완료)
+- [x] WeldingCommon에 ScannerACS 독립 구현
+- [x] Scanner RegisterDevices()에 ACS + 모터 등록
+- [x] UCScannerMotion Stage X 조그 UI 추가
+- [x] SeqComponentTeachingRun MOTF 자리 확보 (주석)
+- [x] _ACS 필드 선언 및 InitDev()에서 null-safe 취득
+- [x] CheckDeviceReady()에 ACS 주석으로 준비
+
+### 이정인 선임 변수 맵 수신 후
+- [ ] ScannerACS.Parameter.cs 변수명 교체
+- [ ] eBuffer 번호 교체
+- [ ] MotorIds.eMotorIdx 축 번호 검증
+- [ ] CheckDeviceReady() DeviceIds.ACS 주석 해제
+- [ ] DoComponentWelding() MOTFStart() 주석 해제
+- [ ] DoComponentWelding() CheckMOTFComp() 주석 해제
+- [ ] Scanner Config UseMarkingOnTheFly 설정 확인
+- [ ] 실제 장비에서 동작 확인
diff --git a/Doc/ToNDH/ScannerACS_작업가이드_26.04.06.md b/Doc/ToNDH/ScannerACS_작업가이드_26.04.06.md
new file mode 100644
index 0000000..5db794d
--- /dev/null
+++ b/Doc/ToNDH/ScannerACS_작업가이드_26.04.06.md
@@ -0,0 +1,347 @@
+# ILSOS PrismaticScanner - ACS 작업 가이드
+> 작성일: 2026-04-06 | 작성자: NDY
+
+---
+
+## 목차
+1. [현재까지 완료된 작업](#1-현재까지-완료된-작업)
+2. [변수 맵 수신 후 교체할 곳](#2-변수-맵-수신-후-교체할-곳)
+3. [홈/서보/알람 버튼 추가 작업](#3-홈서보알람-버튼-추가-작업)
+4. [MOTF 시퀀스 흐름](#4-motf-시퀀스-흐름)
+5. [캘리브레이션](#5-캘리브레이션)
+6. [TODO 체크리스트](#6-todo-체크리스트)
+
+---
+
+## 1. 현재까지 완료된 작업
+
+### 1-1. 프로젝트 구조
+```
+WeldingCommon
+ └── Devices/ACS/
+ ├── ScannerACS.cs ← Gun과 독립된 Scanner 전용 ACS
+ ├── ScannerACS.Parameter.cs ← ACS 변수명 상수 (TODO: 변수 맵 교체 필요)
+ └── MotionVariable.cs ← 모터 상태값
+
+ILSOS_PrismaticScanner
+ └── EquipmentSetup.RegisterDevices()
+ ├── ScannerACS 등록
+ ├── MotorX2 등록 (Stage X)
+ ├── MotorY1, Y2, Z 등록 (TODO: 축 번호 검증 필요)
+ └── EquipDef.cs에 MotorIds 추가
+```
+
+### 1-2. WeldingCommon - UCScannerMotionViewModel.cs 수정 완료
+- `_MotorX` (ACSMotor) 필드 추가 및 연결
+- `StageXPos`, `IsStageXActivated` 프로퍼티 추가
+- `CMD_JogMove`에 `Stage_X_Left`, `Stage_X_Right` 케이스 추가
+- `MoveStageX()` 메서드 추가 (현재: 직접 모터 명령 방식)
+- `OnMonitoring()`에 Stage X 위치 갱신 추가
+
+### 1-3. WeldingCommon - UCScannerMotion.xaml 수정 완료
+- `Stage X Motion (ACS)` GroupBox 추가 (Grid.Row="2")
+- Stage X[mm] 위치 표시
+- ◀ − / + ▶ 조그 버튼
+
+---
+
+## 2. 변수 맵 수신 후 교체할 곳
+
+### 2-1. ScannerACS.Parameter.cs
+```csharp
+// 현재 placeholder → 실제 변수명으로 교체
+const string iMOTFStart = "iPCset_SCANNER_MOTF_START"; // TODO
+const string iMOTFComplete = "iPCget_SCANNER_MOTF_COMP"; // TODO
+const string rMOTFVelocity = "rPCset_SCANNER_MOTF_VEL"; // TODO
+const string rMOTFAcc = "rPCset_SCANNER_MOTF_ACC"; // TODO
+const string iAllAxisHomeComplete = "iPCget_ALLAXIS_HOMECOMP"; // TODO
+const string AlarmReset = "iAlarmreset"; // TODO
+const string HeavyAlarm = "iAlarm"; // TODO
+const string LightAlarm = "iLightAlarm"; // TODO
+
+private enum eBuffer
+{
+ All_Axis_Home = 0, // TODO: 실제 버퍼 번호로 교체
+ MOTF_Start = 11, // TODO: 실제 버퍼 번호로 교체
+}
+```
+
+### 2-2. EquipDef.cs - MotorIds 축 번호 검증
+```csharp
+public enum eMotorIdx
+{
+ AxisX2 = 1, // TODO: 실제 ACS 채널 번호 확인
+ AxisY1 = 3, // TODO: 실제 ACS 채널 번호 확인
+ AxisY2 = 4, // TODO: 실제 ACS 채널 번호 확인
+ AxisZ = 5, // TODO: 실제 ACS 채널 번호 확인
+}
+```
+
+### 2-3. UCScannerMotionViewModel.cs - MoveStageX() 교체
+```csharp
+// 현재 (직접 모터 명령)
+private void MoveStageX(eJogDirection direction)
+{
+ if (_MotorX == null || !_MotorX.IsActivated) return;
+ double step = direction == eJogDirection.PLUS ? -StepValue : +StepValue;
+ _MotorX.SetTargetPos(step, TargetPosType.REL);
+ _MotorX.Move();
+}
+
+// TODO: 변수 맵 수신 후 ACS 버퍼 방식으로 교체
+// private void MoveStageX(eJogDirection direction)
+// {
+// double velocity = GetStageXVelocity(); // Setting에서 가져오기
+// double acc = GetStageXAcc();
+// _acs?.StageXJogStart(velocity, acc);
+// }
+```
+
+---
+
+## 3. 홈/서보/알람 버튼 추가 작업
+
+### 3-1. IScannerACS 인터페이스 추가 항목
+```csharp
+// WeldingCommon/Devices/ACS/ScannerACS.cs
+public interface IScannerACS
+{
+ bool IsActivated();
+
+ // 홈
+ bool AllAxisHomeStart();
+ bool AllAxisHomeStop();
+ bool CheckAllAxisHome();
+
+ // Stage X 조그 (ACS 버퍼 방식) - TODO: 변수 맵 수신 후 구현
+ bool StageXJogStart(double velocity, double acc);
+ bool StageXJogStop();
+
+ // MOTF
+ bool MOTFStart();
+ bool MOTFStop();
+ bool CheckMOTFComp();
+
+ // 레이저
+ bool SetLaserIOUse(bool on);
+ bool IsLaserOn();
+
+ // 알람
+ bool ResetAlarm();
+ void AllAxisServoOff();
+}
+```
+
+### 3-2. UCScannerMotionViewModel.cs - 추가할 코드
+```csharp
+// 필드 추가
+private ScannerACS _acs;
+
+// 커맨드 추가
+public ICommand CMD_HomeStart { get; }
+public ICommand CMD_HomeStop { get; }
+public ICommand CMD_AlarmReset { get; }
+public ICommand CMD_ServoOff { get; }
+
+// 생성자에서 추가
+try { _acs = _Aurora.DeviceManager["ACS"] as ScannerACS; }
+catch { _acs = null; }
+
+CMD_HomeStart = new DelegateCommand(_ => _acs?.AllAxisHomeStart());
+CMD_HomeStop = new DelegateCommand(_ => _acs?.AllAxisHomeStop());
+CMD_AlarmReset = new DelegateCommand(_ => _acs?.ResetAlarm());
+CMD_ServoOff = new DelegateCommand(_ => _acs?.AllAxisServoOff());
+```
+
+### 3-3. UCScannerMotion.xaml - Stage X GroupBox 교체
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+---
+
+## 4. MOTF 시퀀스 흐름
+
+### 4-1. 개념
+```
+MOTF (Mark On The Fly)
+= Stage X가 이동하는 동안 갈바노 스캐너가 레이저 마킹
+
+트리거: Stage X 엔코더 위치 기반
+ → 특정 위치 도달 시 갈바노가 마킹 시작
+```
+
+### 4-2. 시퀀스 흐름 (사수 피드백 기준)
+```
+[Step 1] Scanner List 채우기
+ LASA 레시피 기반으로 마킹 좌표 목록 생성
+ → ScannerList에 마킹할 X, Y 좌표 순서대로 입력
+
+[Step 2] ACS에 파라미터 전송
+ → 시작 위치, 속도, 가속도 설정
+ → 레이저 ON/OFF 위치 설정
+ → MOTF 트리거 조건 설정
+
+[Step 3] ACS MOTF 시작
+ → RunBuffer(eBuffer.MOTF_Start)
+ → Stage X 이동 시작
+ → 엔코더 트리거로 갈바노 동기화
+
+[Step 4] 완료 확인
+ → ReadVariableInt(iMOTFComplete) > 0
+```
+
+### 4-3. 참조할 기존 소스
+```
+CCW 웰더 프로젝트
+ └── SeqAutoRunCCWH52A.cs ← MOTF 시퀀스 구현 참조
+```
+
+### 4-4. 구현 예정 시퀀스 클래스 구조
+```csharp
+// ILSOS_PrismaticScanner/Sequences/SeqAutoRunPrismaticScanner.cs
+public class SeqAutoRunPrismaticScanner : ISequence
+{
+ private ScannerACS _acs;
+ private Scanner _scanner;
+
+ protected override void DoProcess()
+ {
+ // Step 1. Scanner List 채우기 (LASA 레시피 기반)
+ FillScannerList();
+
+ // Step 2. ACS 파라미터 전송
+ SetMOTFParameters();
+
+ // Step 3. MOTF 시작
+ _acs.MOTFStart();
+
+ // Step 4. 완료 대기
+ WaitForComplete();
+ }
+
+ private void FillScannerList()
+ {
+ // TODO: LASA 레시피에서 마킹 좌표 가져오기
+ // var recipe = ... (MainRecipe)
+ // scanner.SetMarkList(recipe.MarkPoints);
+ }
+
+ private void SetMOTFParameters()
+ {
+ // TODO: 변수 맵 수신 후 실제 변수명으로 구현
+ // _acs.SetMOTFVelocity(velocity);
+ // _acs.SetMOTFAcc(acc);
+ // _acs.SetLaserOnOffPosition(onPos, offPos);
+ }
+
+ private void WaitForComplete()
+ {
+ // TODO: Timeout 처리 포함
+ // while (!_acs.CheckMOTFComp()) { Thread.Sleep(10); }
+ }
+}
+```
+
+### 4-5. MOTF 좌표 오류 주의사항
+```
+주의: MOTF 트리거는 Stage X 엔코더만 기준
+ → 헤드 유닛 Y축 이동은 미반영
+ → R-section(라운드 코너)에서 위치 오차 발생 가능
+
+원인:
+ Stage X + 갈바노 X + 갈바노 Y 조합으로 실제 마킹 위치 결정
+ → Stage X 엔코더 트리거만으로는 Y 성분 누락
+
+해결: 이정인 선임과 R-section 처리 방법 협의 필요
+```
+
+---
+
+## 5. 캘리브레이션
+
+### 5-1. 방향
+```
+기존 소스와 동일하게 진행 (사수 피드백)
+→ WeldingCommon의 LaserPowerCalParam 그대로 사용
+→ ScannerACS.DoActivate()에서 이미 참조 구조 잡혀있음
+```
+
+### 5-2. 현재 ScannerACS.cs 상태
+```csharp
+// DoActivate()에서 캘리브레이션 참조 준비됨
+// TODO: Scanner용 SettingIds 확정 후 연결
+// _calibration = env.SettingEntry[CommonSettingIds.LaserPowerCalParam]
+// as LaserPowerCalParam;
+```
+
+---
+
+## 6. TODO 체크리스트
+
+### 이정인 선임 변수 맵 수신 전
+- [ ] 홈/서보/알람 버튼 XAML 추가
+- [ ] UCScannerMotionViewModel에 CMD_HomeStart 등 커맨드 추가
+- [ ] SeqAutoRunPrismaticScanner 클래스 골격 작성
+
+### 이정인 선임 변수 맵 수신 후
+- [ ] ScannerACS.Parameter.cs 변수명 교체
+- [ ] eBuffer 번호 교체
+- [ ] MotorIds.eMotorIdx 축 번호 검증
+- [ ] MoveStageX() → ACS 버퍼 방식으로 교체
+- [ ] MOTF 파라미터 전송 구현
+- [ ] 실제 장비에서 동작 확인
+
+### 장기 과제
+- [ ] R-section 위치 오차 처리 방법 협의
+- [ ] 캘리브레이션 연결
+- [ ] Scanner + ACS 통합 컨트롤러 클래스 설계
diff --git a/Machines/ILSOS_PrismaticScanner/EquipmentSetup.cs b/Machines/ILSOS_PrismaticScanner/EquipmentSetup.cs
index b69f6e8..507e852 100644
--- a/Machines/ILSOS_PrismaticScanner/EquipmentSetup.cs
+++ b/Machines/ILSOS_PrismaticScanner/EquipmentSetup.cs
@@ -305,6 +305,9 @@ namespace ILSOS_PrismaticScanner
//manual.AddSubMenu(CommonViewIds.LBAD, new UCLBADDevice(AuroraEnv, CommonDeviceIds.LBAD, CommonDeviceIds.IODevice));
//manual.AddSubMenu(CommonViewIds.ConvEdgeObject, new UCConvEdgeObject(AuroraEnv));
manual.AddSubMenu(CommonViewIds.ModelFinder, new UCManualModelFinder(new UCManualModelFinderViewModel(AuroraEnv)));
+ #region 26.04.06 ACS 모션 스테이지 메뉴얼 추가 - 진행 예정이였으나, Gun 에 속해서 사용 애매함. View 자체를 전부 가져오지는 못함.
+ #endregion
+
// Manual - For Test
//manual.AddSubMenu(CommonViewIds.TestDevice, new UCTestDevice(new UCTestDeviceViewModel(AuroraEnv)));
diff --git a/Machines/ILSOS_PrismaticScanner/Sequences/Manual/SeqComponentTeachingRun.cs b/Machines/ILSOS_PrismaticScanner/Sequences/Manual/SeqComponentTeachingRun.cs
index 5e7b0d3..e8ae5bd 100644
--- a/Machines/ILSOS_PrismaticScanner/Sequences/Manual/SeqComponentTeachingRun.cs
+++ b/Machines/ILSOS_PrismaticScanner/Sequences/Manual/SeqComponentTeachingRun.cs
@@ -33,6 +33,7 @@ using VisionCommon;
using WeldingCommon;
using WeldingCommon.Alarm;
using WeldingCommon.Data;
+using WeldingCommon.Devices.ACS; // 26.04.06
using WeldingCommon.Sequences;
using WeldingCommon.Settings;
using WeldingCommon.Utils;
@@ -78,6 +79,10 @@ namespace ILSOS_PrismaticScanner.Sequences
private static Scanner _Scanner;
private static PAIX_PrismaticScanner _IO;
+
+ // 필드 선언 추가 (다른 필드들 옆에)
+ private ScannerACS _ACS; // 26.04.06 NDY - MOTF용 ACS Stage X 제어
+
private Laser _Laser;
private ILaser _LaserDevice;
private LaserTruFiber _LaserTruFiber;
@@ -194,11 +199,15 @@ namespace ILSOS_PrismaticScanner.Sequences
_Scanner = context.GetLaserScanner();
_IO = context.Devices[CommonDeviceIds.IODevice] as PAIX_PrismaticScanner;
+ // 26.04.06 NDY 추가: ACS 디바이스 취득 (null-safe)
+ // 변수 맵 수신 후 MOTFStart() 호출에 사용
+ try { _ACS = context.Devices["ACS"] as ScannerACS; }
+ catch { _ACS = null; Log.W("ACS 디바이스 취득 실패 (null 처리)"); }
}
private bool CheckDeviceReady(SequenceContext context)
{
List deviceListToCheck = new List() { CommonDeviceIds.Scanner, CommonDeviceIds.IODevice };
-
+ //List deviceListToCheck = new List() { CommonDeviceIds.Scanner, CommonDeviceIds.IODevice,DeviceIds.ACS }; 변수 맵 수신 후 ACS 주석 해제 위에 주석 처리
bool isAllDeviceActivated = true;
foreach (string item in deviceListToCheck) {
if (!context.Devices[item].IsActivated) {
@@ -505,7 +514,24 @@ namespace ILSOS_PrismaticScanner.Sequences
Log.I($"Welding List Space : {_Scanner.GetListSpace()}");
DateTime weldingTime = DateTime.Now;
_Scanner.ExecuteList(_WeldingScannerListNo);
+
+
+ // ── 26.04.06 NDY 추가: ACS Stage X 이동 ──────────────
+ // MOTF: Scanner List 실행과 동시에 Stage X 이동 시작
+ // Stage X 엔코더가 트리거 → WaitForEncoderX 조건 도달 시 갈바노 마킹
+ // TODO: 변수 맵 수신 후 _ACS.MOTFStart()로 교체
+ // _ACS?.MOTFStart();
+ // ────────────────────────────────────────────────────
+
+
+
_Scanner.WaitForListTaskCompleted(_SystemParameter.WeldLinesTaskTimeOut);
+
+ // ── 26.04.06 NDY 추가: ACS 완료 대기 ─────────────────
+ // TODO: 변수 맵 수신 후 활성화
+ // while (!_ACS.CheckMOTFComp()) { Thread.Sleep(10); }
+ // ────────────────────────────────────────────────────
+
_MeasurementSpanTime = DateTime.Now.Subtract(weldingTime);
Log.D(string.Format($"[ComponentTeachingRun] Component Welding Complete Time : {_MeasurementSpanTime.TotalMilliseconds}"));
diff --git a/WeldingComponents/WeldingCommon/Devices/ACS/PrismaticGunACS.Parameter.cs b/WeldingComponents/WeldingCommon/Devices/ACS/PrismaticGunACS.Parameter.cs
index 8d0cd77..21ce72d 100644
--- a/WeldingComponents/WeldingCommon/Devices/ACS/PrismaticGunACS.Parameter.cs
+++ b/WeldingComponents/WeldingCommon/Devices/ACS/PrismaticGunACS.Parameter.cs
@@ -1,4 +1,7 @@
// WeldingCommon/Devices/ACS/ScannerACS.Parameter.cs
+using AuroraACS;
+using System.Web.UI.WebControls;
+
namespace WeldingCommon.Devices.ACS
{
public partial class ScannerACS
@@ -19,6 +22,19 @@ namespace WeldingCommon.Devices.ACS
const string rMOTFVelocity = "rPCset_SCANNER_MOTF_VEL"; // placeholder
const string rMOTFAcc = "rPCset_SCANNER_MOTF_ACC"; // placeholder
+ #region 변수 맵 받으면 작업 순서
+ //1. ScannerACS.Parameter.cs
+ // → placeholder 변수명을 실제 변수명으로 교체
+
+ //2. ScannerACS.cs
+ // → 축 번호 검증(ACSMotor 생성 시 넣은 번호)
+
+ //3. EquipDef.cs
+ // → MotorIds의 eMotorIdx 번호 검증
+
+ //4. 빌드 후 실제 장비에서 동작 확인
+ #endregion
+
// ── 레이저 ───────────────────────────────────────
const string IsLaserOn = "LCI_DEVICE.LaserEnabled";
const string iLaserUse = "iPCset_LASER_USE";
diff --git a/WeldingComponents/WeldingCommon/EquipDefCommon.cs b/WeldingComponents/WeldingCommon/EquipDefCommon.cs
index 6cf1485..b658615 100644
--- a/WeldingComponents/WeldingCommon/EquipDefCommon.cs
+++ b/WeldingComponents/WeldingCommon/EquipDefCommon.cs
@@ -65,6 +65,7 @@
public const string TestDevice = "TestDevice"; // For Test Device
public const string TestSequence = "TestSequence"; // For Test Sequence
+ public const string StageManual = "StageManual"; // 26.04.06
// Monitoring
public const string PLCBitMonitoring = "PLCBitMonitoring";
diff --git a/WeldingComponents/WeldingCommon/Sequences/Main/SeqUploadWeldingList.cs b/WeldingComponents/WeldingCommon/Sequences/Main/SeqUploadWeldingList.cs
index c3ec4fa..bf14ec1 100644
--- a/WeldingComponents/WeldingCommon/Sequences/Main/SeqUploadWeldingList.cs
+++ b/WeldingComponents/WeldingCommon/Sequences/Main/SeqUploadWeldingList.cs
@@ -87,7 +87,7 @@ namespace WeldingCommon.Sequences
sb.AddAction(SEQ_ENTER, Sequence_Enter);
sb.AddDecision(SEQ_CHECK_INIT_CONDITION, CheckInitCondition); //분기
- sb.AddAction(SEQ_MARKING_ON_THE_FLY_ON, MarkingOnTheFlyOn);
+ sb.AddAction(SEQ_MARKING_ON_THE_FLY_ON, MarkingOnTheFlyOn);
sb.AddAction(SEQ_RECORD_SIGNAL_ON, RecordSignalOn);
sb.AddAction(SEQ_LOAD_DRAW_OBJECT_SCANNER_PARAM, LoadDrawObjectScannerParam);
sb.AddAction(SEQ_MARKING_ON_THE_FLY_OFF, MarkingOnTheFlyOff);
@@ -225,6 +225,7 @@ namespace WeldingCommon.Sequences
return SEQ_MARKING_ON_THE_FLY_ON;
}
+ // MOTF 지원 중
private SequenceActionResult MarkingOnTheFlyOn(SequenceContext context, string id, SequenceActionResult param)
{
if (_Scanner.Config.UseMarkingOnTheFly) {
diff --git a/WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerMotion.xaml b/WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerMotion.xaml
index 582cb28..35ee47d 100644
--- a/WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerMotion.xaml
+++ b/WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerMotion.xaml
@@ -24,38 +24,47 @@
-
-
-
+
+
+
+
+
-
-
-
+
+
+
-
+
-
-
+
+
-
-
+
+
+
+
-
-
+
+
-
-
+
+
-
+
@@ -68,84 +77,116 @@
-
-
-
-
-
+
+
+
+
+
-
-
+
+
-
-
-
-
+
+
+
+
Motion Speed
Motion Mode
-
-
-
+
+
+
+ Style="{StaticResource stRadio}" Content="LOW" Margin="3"/>
+ Style="{StaticResource stRadio}" Content="MID" IsChecked="True" Margin="3"/>
+ Style="{StaticResource stRadio}" Content="HIGH" Margin="3"/>
-
-
+
+
+ Style="{StaticResource stRadio}" Content="ABS" Margin="3" IsChecked="True"/>
+ Style="{StaticResource stRadio}" Content="REL" Margin="3"/>
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
- MinLimit
-
-
-
- MaxLimit
-
-
-
-
-
-
-
-
-
+ MinLimit
+
+
+ MaxLimit
+
+
+
+
+
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerMotionViewModel.cs b/WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerMotionViewModel.cs
index 7ef6295..194c115 100644
--- a/WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerMotionViewModel.cs
+++ b/WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerMotionViewModel.cs
@@ -1,7 +1,9 @@
using Aurora;
using Aurora.Api;
+using Aurora.Api.Devices;
using Aurora.Api.Manager;
using Aurora.View;
+using AuroraACS; // 26.04.06 NDY 추가
using AuroraLaser;
using AuroraLaserScanner;
using HarfBuzzSharp;
@@ -41,13 +43,10 @@ namespace WeldingCommon.Views
public ICommand CMD_VisibleChanged { get; }
public ICommand CMD_SpeedChanged { get; }
public ICommand CMD_MotionModeChanged { get; }
-
public ICommand CMD_JogMove { get; }
public ICommand CMD_CommandPosMove { get; }
-
private readonly IAuroraEnvironment _Aurora;
-
private readonly Scanner _Scanner;
private readonly Laser _Laser;
private readonly ScannerConfig _ScannerConfig;
@@ -82,6 +81,16 @@ namespace WeldingCommon.Views
private readonly DispatcherTimer _MonitorTimer;
+ // 26.04.06 NDY Stage X 관련 필드 추가 ==================
+ private ACSMotor _MotorX;
+
+ private double _StageXPos;
+ public double StageXPos { get => _StageXPos; set => SetProperty(ref _StageXPos, value); }
+
+ private bool _IsStageXActivated;
+ public bool IsStageXActivated { get => _IsStageXActivated; set => SetProperty(ref _IsStageXActivated, value); }
+ // ========================================================
+
// PAM, RUW 처럼 2개 이상의 scanner 를 사용하는 경우가 있어, scannerId 를 받는 형태로 생성
public UCScannerMotionViewModel(IAuroraEnvironment aurora, string scannerId)
{
@@ -91,6 +100,11 @@ namespace WeldingCommon.Views
_Laser = _Aurora.DeviceManager[CommonDeviceIds.Laser] as Laser;
_ScannerConfig = _Aurora.DeviceManager.GetDeviceConfig(scannerId) as ScannerConfig;
+ // 26.04.06 NDY Stage X 모터 연결 추가 ===============
+ try { _MotorX = _Aurora.DeviceManager["AxisX2"] as ACSMotor; }
+ catch { _MotorX = null; }
+ // ====================================================
+
InitVariable();
CMD_VisibleChanged = new DelegateCommand(VisibleChanged);
@@ -113,157 +127,139 @@ namespace WeldingCommon.Views
private void VisibleChanged(object obj)
{
bool visible = (bool)obj;
- if (visible) {
+ if (visible)
+ {
Init_XYLimitPos();
_MonitorTimer.Start();
- } else {
+ }
+ else
+ {
_MonitorTimer.Stop();
}
}
private void Init_XYLimitPos()
{
- // TODO: Limit 값을 Min, Max 로 따로 Setting 입력값으로 가져와야 할 수도 있음. 우선 현상태 유지
CommandPosLimitMinX = -1 * _ScannerConfig.ScanXSize / 2;
CommandPosLimitMaxX = _ScannerConfig.ScanXSize / 2;
-
CommandPosLimitMinY = -1 * _ScannerConfig.ScanYSize / 2;
CommandPosLimitMaxY = _ScannerConfig.ScanYSize / 2;
-
- //CommandPosXLimit = string.Format($"{-1 * _ScannerConfig.ScanXSize / 2} ~ {_ScannerConfig.ScanXSize / 2}");
- //CommandPosYLimit = string.Format($"{-1 * _ScannerConfig.ScanYSize / 2} ~ {_ScannerConfig.ScanYSize / 2}");
}
private void OnMonitoring(object sender, EventArgs e)
{
+ // 기존 Scanner 모니터링
double xPos, yPos;
- if (_Scanner != null && _Scanner.IsActivated) {
+ if (_Scanner != null && _Scanner.IsActivated)
+ {
_Scanner.GetXYPos(out xPos, out yPos);
ScannerXPos = xPos;
ScannerYPos = yPos;
-
IsScannerBusy = _Scanner.GetBusyStatus();
- } else {
+ }
+ else
+ {
ScannerXPos = 0;
ScannerYPos = 0;
IsScannerBusy = false;
}
+ // 26.04.06 NDY Stage X 모니터링 추가 ================
+ if (_MotorX != null && _MotorX.IsActivated)
+ {
+ StageXPos = _MotorX.GetActualPos();
+ IsStageXActivated = true;
+ }
+ else
+ {
+ IsStageXActivated = false;
+ }
+ // ====================================================
}
private void PerformCmdSpeedChanged(object commandParameter)
{
- switch ((string)commandParameter) {
- case "Low": {
- _VelocityRate = 0.5;
- }
- break;
-
- case "Mid": {
- _VelocityRate = 1;
- }
- break;
-
- case "High": {
- _VelocityRate = 1.5;
- }
- break;
+ switch ((string)commandParameter)
+ {
+ case "Low": _VelocityRate = 0.5; break;
+ case "Mid": _VelocityRate = 1; break;
+ case "High": _VelocityRate = 1.5; break;
}
}
private void PerformCmdMotionModeChanged(object commandParameter)
{
- switch ((string)commandParameter) {
- case "ABS": {
- _MotionMode = eMotionMode.ABS;
- }
- break;
-
- case "REL": {
- _MotionMode = eMotionMode.REL;
- }
- break;
+ switch ((string)commandParameter)
+ {
+ case "ABS": _MotionMode = eMotionMode.ABS; break;
+ case "REL": _MotionMode = eMotionMode.REL; break;
}
}
private void PerformCmdJogMove(object commandParameter)
{
- // TODO: Left, Right, Up, Down 은 장비 Scanner 의 방향에 따라 달라질 수 있다
- // 양산중에는 동작 하면 안됨
var MainSequence = _Aurora.SequenceEntry[CommonSequenceIds.SeqAutoRun].RunState;
- if(MainSequence == SequenceRunState.Running)
+ if (MainSequence == SequenceRunState.Running)
return;
- switch ((string)commandParameter) {
- case "Left": {
- MoveJogXYPos(eAxisType.AXIS_X, eJogDirection.MINUS);
- }
- break;
+ switch ((string)commandParameter)
+ {
+ case "Left": MoveJogXYPos(eAxisType.AXIS_X, eJogDirection.MINUS); break;
+ 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;
- 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;
+ // 26.04.06 NDY Stage X 버튼 추가 =================
+ case "Stage_X_Left": MoveStageX(eJogDirection.MINUS); break;
+ case "Stage_X_Right": MoveStageX(eJogDirection.PLUS); break;
+ // =================================================
}
}
+ // 26.04.06 NDY Stage X 이동 메서드 추가 ==================
+ private void MoveStageX(eJogDirection direction)
+ {
+ if (_MotorX == null || !_MotorX.IsActivated) return;
+
+ // 이미지 기준 방향 반전 적용
+ double step = direction == eJogDirection.PLUS ? -StepValue : +StepValue;
+ Log.I($"Stage X Move : {step}mm");
+
+ _MotorX.SetTargetPos(step, TargetPosType.REL);
+ _MotorX.Move();
+ }
+ // =========================================================
+
private void MoveJogXYPos(eAxisType axisType, eJogDirection direction)
{
double xPos = 0.0;
double yPos = 0.0;
- switch (axisType) {
- case eAxisType.AXIS_X: {
- if (direction == eJogDirection.MINUS) {
- xPos = StepValue * -1;
- yPos = 0;
- } else if (direction == eJogDirection.PLUS) {
- xPos = StepValue;
- yPos = 0;
- }
+ switch (axisType)
+ {
+ case eAxisType.AXIS_X:
+ {
+ if (direction == eJogDirection.MINUS) { xPos = StepValue * -1; yPos = 0; }
+ else if (direction == eJogDirection.PLUS) { xPos = StepValue; yPos = 0; }
}
break;
-
- case eAxisType.AXIS_Y: {
- if (direction == eJogDirection.MINUS) {
- xPos = 0;
- yPos = StepValue * -1;
- } else if (direction == eJogDirection.PLUS) {
- xPos = 0;
- yPos = StepValue;
- }
+ case eAxisType.AXIS_Y:
+ {
+ if (direction == eJogDirection.MINUS) { xPos = 0; yPos = StepValue * -1; }
+ else if (direction == eJogDirection.PLUS) { xPos = 0; yPos = StepValue; }
}
break;
-
- default:
- break;
}
- // 현재 Curruent Pos 기준으로, 입력방향으로 입력값이 Limit 를 넘어 서는지 확인 (Rel 임)
- if (CheckOverLimitPos(ScannerXPos + xPos, ScannerYPos + yPos)) {
- Log.E(string.Format("움직이려는 범위가 스캐너 범위를 초과하였습니다."));
+ if (CheckOverLimitPos(ScannerXPos + xPos, ScannerYPos + yPos))
+ {
+ Log.E("움직이려는 범위가 스캐너 범위를 초과하였습니다.");
return;
}
double finalVelocity = _ScannerConfig.JumpSpeed * _VelocityRate;
_Scanner.SetJumpSpeedCtrl(finalVelocity);
_Scanner.JumpRel(xPos, yPos);
-
- //Jump 후 Speed 원복
_Scanner.SetJumpSpeedCtrl(_ScannerConfig.JumpSpeed);
}
@@ -272,23 +268,15 @@ namespace WeldingCommon.Views
double finalVelocity = _ScannerConfig.JumpSpeed * _VelocityRate;
_Scanner.SetJumpSpeedCtrl(finalVelocity);
_Scanner.JumpAbs(0, 0);
-
- //Jump 후 Speed 원복
_Scanner.SetJumpSpeedCtrl(_ScannerConfig.JumpSpeed);
}
private void PerformCmdCommandPosMove(object commandParameter)
{
- switch ((string)commandParameter) {
- case "X": {
- MoveCommandXYPos(eAxisType.AXIS_X);
- }
- break;
-
- case "Y": {
- MoveCommandXYPos(eAxisType.AXIS_Y);
- }
- break;
+ switch ((string)commandParameter)
+ {
+ case "X": MoveCommandXYPos(eAxisType.AXIS_X); break;
+ case "Y": MoveCommandXYPos(eAxisType.AXIS_Y); break;
}
}
@@ -296,42 +284,35 @@ namespace WeldingCommon.Views
{
double xPos = 0.0;
double yPos = 0.0;
- switch (axisType) {
- case eAxisType.AXIS_X: {
+ switch (axisType)
+ {
+ case eAxisType.AXIS_X:
+ {
xPos = CommandPosX;
-
- if (_MotionMode == eMotionMode.ABS) {
- yPos = _Scanner.GetCommandPosY();
- } else {
- yPos = 0.0;
- }
+ yPos = _MotionMode == eMotionMode.ABS ? _Scanner.GetCommandPosY() : 0.0;
}
break;
-
- case eAxisType.AXIS_Y: {
- if (_MotionMode == eMotionMode.ABS) {
- xPos = _Scanner.GetCommandPosX();
- } else {
- xPos = 0.0;
- }
-
+ case eAxisType.AXIS_Y:
+ {
+ xPos = _MotionMode == eMotionMode.ABS ? _Scanner.GetCommandPosX() : 0.0;
yPos = CommandPosY;
}
break;
-
- default:
- break;
}
- // 현재 Curruent Pos 기준으로, 입력방향으로 입력값이 Limit 를 넘어 서는지 확인
- if (_MotionMode == eMotionMode.ABS) {
- if (CheckOverLimitPos(xPos, yPos)) {
- Log.E(string.Format("움직이려는 범위가 스캐너 범위를 초과하였습니다."));
+ if (_MotionMode == eMotionMode.ABS)
+ {
+ if (CheckOverLimitPos(xPos, yPos))
+ {
+ Log.E("움직이려는 범위가 스캐너 범위를 초과하였습니다.");
return;
}
- } else {
- if (CheckOverLimitPos(ScannerXPos + xPos, ScannerYPos + yPos)) {
- Log.E(string.Format("움직이려는 범위가 스캐너 범위를 초과하였습니다."));
+ }
+ else
+ {
+ if (CheckOverLimitPos(ScannerXPos + xPos, ScannerYPos + yPos))
+ {
+ Log.E("움직이려는 범위가 스캐너 범위를 초과하였습니다.");
return;
}
}
@@ -339,25 +320,19 @@ namespace WeldingCommon.Views
double finalVelocity = _ScannerConfig.JumpSpeed * _VelocityRate;
_Scanner.SetJumpSpeedCtrl(finalVelocity);
- if (_MotionMode == eMotionMode.ABS) {
+ if (_MotionMode == eMotionMode.ABS)
_Scanner.JumpAbs(xPos, yPos);
- } else {
+ else
_Scanner.JumpRel(xPos, yPos);
- }
- //Jump 후 Speed 원복
_Scanner.SetJumpSpeedCtrl(_ScannerConfig.JumpSpeed);
}
private bool CheckOverLimitPos(double calcPosX, double calcPosY)
{
- if (calcPosX < CommandPosLimitMinX || calcPosX > CommandPosLimitMaxX) {
- return true;
- } else if (calcPosY < CommandPosLimitMinY || calcPosY > CommandPosLimitMaxY) {
- return true;
- }
-
+ if (calcPosX < CommandPosLimitMinX || calcPosX > CommandPosLimitMaxX) return true;
+ if (calcPosY < CommandPosLimitMinY || calcPosY > CommandPosLimitMaxY) return true;
return false;
}
}
-}
+}
\ No newline at end of file