feat: WeldingCommon ACS Stage Manual View 추가 및 MOTF 준비

- WeldingCommon/Devices/ACS/ScannerACS.cs
  Gun(PrismaticGunACS) 기반 Scanner 전용 ACS 구현
  (IScannerACS 인터페이스 + MOTF 3개 메서드 추가)

- WeldingCommon/Devices/ACS/ScannerACS.Parameter.cs
  Gun Parameter 기반 변수명 통합 + eOutType enum 추가
  (MOTF 변수명 placeholder - 변수 맵 수신 후 교체 필요)

- WeldingCommon/Views/ACS/Stage/
  UCStageManual, UCMotorAxses, UCStageMotorMoving
  Gun Stage View 기반 Scanner X축 전용으로 단순화
  Manual 메뉴에 Stage Manual 서브메뉴 추가

- SeqComponentTeachingRun.cs
  _ACS(IScannerACS) 필드 선언 및 InitDev() 취득
  MOTFStart() / CheckMOTFComp() 주석으로 자리 확보

TODO: 이정인 선임 ACS 변수 맵 수신 후
  - ScannerACS.Parameter.cs 변수명/버퍼 번호 교체
  - MOTFStart() / CheckMOTFComp() 주석 해제
  - Stage Manual 시퀀스/설정 추가 여부 확인
  - Y/Z축 제어 필요 여부 확인
This commit is contained in:
노도연 2026-04-06 17:26:25 +09:00
parent 09446d51e2
commit 8d1af44743
14 changed files with 1415 additions and 247 deletions

View File

@ -54,6 +54,7 @@ using static WeldingCommon.Vision.Factory.VisionFactory;
using AuroraACS;
using AuroraACS.Views;
using AuroraMotor;
using WeldingCommon.Views.ACS;
namespace ILSOS_PrismaticScanner
@ -271,9 +272,10 @@ namespace ILSOS_PrismaticScanner
base.RegisterViews(manager);
// Auto Run
var MainAutoRun = new MainMenu(CommonViewIds.AutoRun, MenuIcon.GetMenuIcon(eMenuIcon.HOME), PermissionLevel.NONE, false);
MainAutoRun.AddSubMenu(CommonViewIds.AutoRun, new UCMainAutoRun(new UCMainAutoRunViewModel(AuroraEnv, SequenceIds.SeqAutoRun)));
manager.RegisterMainMenu(MainAutoRun);
// 26.04.06 NDY: Manual 서브로 이동
//var MainAutoRun = new MainMenu(CommonViewIds.AutoRun, MenuIcon.GetMenuIcon(eMenuIcon.HOME), PermissionLevel.NONE, false);
//MainAutoRun.AddSubMenu(CommonViewIds.AutoRun, new UCMainAutoRun(new UCMainAutoRunViewModel(AuroraEnv, SequenceIds.SeqAutoRun)));
//manager.RegisterMainMenu(MainAutoRun);
// Recipe
MainMenu recipe = new MainMenu(CommonViewIds.Recipe, MenuIcon.GetMenuIcon(eMenuIcon.RECIPE), PermissionLevel.NONE, true);
@ -300,12 +302,17 @@ namespace ILSOS_PrismaticScanner
// Manual
//var manual = new MainMenu(CommonViewIds.Manual, MenuIcon.GetMenuIcon(eMenuIcon.DEVICE), PermissionLevel.LEVEL4, true); // Level4 가 맞으나, 개발때는 None 으로 뒀다가 완료되면 풀자.
var manual = new MainMenu(CommonViewIds.Manual, MenuIcon.GetMenuIcon(eMenuIcon.CAMERA), PermissionLevel.NONE, true);
// 26.04.06 NDY 추가: AutoRun을 Manual 서브메뉴로 추가
manual.AddSubMenu(CommonViewIds.AutoRun, new UCMainAutoRun(new UCMainAutoRunViewModel(AuroraEnv, SequenceIds.SeqAutoRun))); // 추가
manual.AddSubMenu(CommonViewIds.ScannerOperation, new UCManualScannerOperation(new UCManualScannerOperationViewModel(AuroraEnv, CommonDeviceIds.Scanner, false)));
//manual.AddSubMenu(CommonViewIds.OCT, new UCOCTManualMotion(AuroraEnv, CommonDeviceIds.OCT));
//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 자체를 전부 가져오지는 못함.
#region 26.04.06 ACS 모션 스테이지 메뉴얼 추가 - 진행 예정이였으나, Gun 에 속해서 사용 애매함. View 자체를 전부 가져오지는 못함. - Gun의 view도 common에 복사
// Manual 메뉴에 추가
manual.AddSubMenu(CommonViewIds.StageManual,
new UCStageManual(AuroraEnv, AuroraEnv.DeviceManager));
#endregion

View File

@ -81,8 +81,11 @@ namespace ILSOS_PrismaticScanner.Sequences
private static PAIX_PrismaticScanner _IO;
// 필드 선언 추가 (다른 필드들 옆에)
private ScannerACS _ACS; // 26.04.06 NDY - MOTF용 ACS Stage X 제어
// 변경 전
//private ScannerACS _ACS;
// 변경 후
private IScannerACS _ACS; // 26.04.06 NDY - MOTF용 ACS Stage X 제어
private Laser _Laser;
private ILaser _LaserDevice;
private LaserTruFiber _LaserTruFiber;
@ -201,7 +204,10 @@ namespace ILSOS_PrismaticScanner.Sequences
// 26.04.06 NDY 추가: ACS 디바이스 취득 (null-safe)
// 변수 맵 수신 후 MOTFStart() 호출에 사용
try { _ACS = context.Devices["ACS"] as ScannerACS; }
// 변경 전
//try { _ACS = context.Devices[DeviceIds.ACS] as ScannerACS; }
// 변경 후
try { _ACS = context.Devices[DeviceIds.ACS] as IScannerACS; }
catch { _ACS = null; Log.W("ACS 디바이스 취득 실패 (null 처리)"); }
}
private bool CheckDeviceReady(SequenceContext context)

View File

@ -1,54 +0,0 @@
// WeldingCommon/Devices/ACS/ScannerACS.Parameter.cs
using AuroraACS;
using System.Web.UI.WebControls;
namespace WeldingCommon.Devices.ACS
{
public partial class ScannerACS
{
// ── 알람 ──────────────────────────────────────────
const string AlarmReset = "iAlarmreset";
const string HeavyAlarm = "iAlarm";
const string LightAlarm = "iLightAlarm";
const string AlarmCodes = "iPCAlarmNo";
// ── 홈 ───────────────────────────────────────────
const string iAllAxisHomeComplete = "iPCget_ALLAXIS_HOMECOMP";
// ── MOTF (Mark On The Fly) ────────────────────────
// TODO: 이정인 선임 ACS 변수 맵 수신 후 실제 변수명으로 교체
const string iMOTFStart = "iPCset_SCANNER_MOTF_START"; // placeholder
const string iMOTFComplete = "iPCget_SCANNER_MOTF_COMP"; // placeholder
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";
// ── 축 번호 ──────────────────────────────────────
const string iXAxisNumber = "iPCget_LCI_ASMASK_X";
// ── 버퍼 ─────────────────────────────────────────
private enum eBuffer
{
All_Axis_Home = 0,
// TODO: Scanner MOTF 버퍼 번호 확인 후 수정
MOTF_Start = 11,
}
}
}

View File

@ -1,185 +0,0 @@
// WeldingCommon/Devices/ACS/ScannerACS.cs
using Aurora;
using Aurora.Api;
using Aurora.Api.Devices;
using Aurora.Util.HislogPlus;
using AuroraACS;
using ACS.SPiiPlusNET;
using Hermes;
using System.Windows;
using WeldingCommon.Device.ACS;
using WeldingCommon.Settings;
namespace WeldingCommon.Devices.ACS
{
public interface IScannerACS
{
bool IsActivated();
// 홈
bool AllAxisHomeStart();
bool AllAxisHomeStop();
bool CheckAllAxisHome();
// MOTF
bool MOTFStart();
bool MOTFStop();
bool CheckMOTFComp();
// 레이저
bool SetLaserIOUse(bool on);
bool IsLaserOn();
// 알람
bool ResetAlarm();
void AllAxisServoOff();
}
public partial class ScannerACS : ACSDevice, IScannerACS
{
// TODO: Scanner용 Setting ID 확정 후 교체
private ACSDeviceSetting _setting;
private ACSMotor MotorStageX; // AxisX2 = Scanner Stage X
public MotionVariable _motorVariable;
public bool HasHeavyAlarm { get; private set; }
public bool HasLightAlarm { get; private set; }
public int[] HaveAlarmCodes { get => _HaveAlarmCodes; }
private int[] _HaveAlarmCodes = new int[11];
public ScannerACS(bool isSimulation, bool useTerminateUMD = false)
: base(isSimulation, useTerminateUMD)
{
_motorVariable = new MotionVariable();
}
protected override int DoActivate(IDeviceConfig config)
{
if (base.DoActivate(config) != E1.OK)
return E2.ABORTED;
var env = Application.Current.AuroraEnv();
// TODO: Scanner용 SettingIds 확정 후 교체
// _setting = env.SettingEntry[ScannerSettingIds.ACS] as ACSDeviceSetting;
// Stage X 모터 취득 (null-safe)
// TODO: 이정인 선임 변수 맵 수신 후 축 번호 검증
try { MotorStageX = env.DeviceManager[CommonDeviceIds.AxisX2] as ACSMotor; }
catch { MotorStageX = null; }
SetMonitoringService(UpdateACSParam, 50);
StartMonitoringService();
return E1.OK;
}
private void UpdateACSParam()
{
// Stage X 상태 갱신
if (MotorStageX != null)
{
_motorVariable.X2AxisPosition = MotorStageX.GetActualPos();
_motorVariable.X2InPosition = MotorStageX.IsInpos();
_motorVariable.X2AxisVelocity = MotorStageX.GetFeedbackVelocity();
_motorVariable.X2AxisAcceleration = MotorStageX.GetAcceleration();
_motorVariable.IsHome = MotorStageX.GetServoStatus() == 1;
}
// 알람 상태 갱신
HasHeavyAlarm = ReadVariableInt(HeavyAlarm) == 1;
HasLightAlarm = ReadVariableInt(LightAlarm) == 1;
ReadVariableIntArrRef(AlarmCodes, ref _HaveAlarmCodes,
ProgramBuffer.ACSC_NONE, 0, 10);
}
protected override int DoDeactivate()
{
StopMonitoringService();
return base.DoDeactivate();
}
// ── 홈 ───────────────────────────────────────────
bool IScannerACS.AllAxisHomeStart()
{
if (RunBuffer((ProgramBuffer)eBuffer.All_Axis_Home))
{
Log.D("AllAxisHomeStart >> Complete");
return true;
}
Log.D("AllAxisHomeStart >> Fail");
return false;
}
bool IScannerACS.AllAxisHomeStop()
{
StopBuffer((ProgramBuffer)eBuffer.All_Axis_Home);
Log.D("AllAxisHomeStop >> Complete");
return true;
}
bool IScannerACS.CheckAllAxisHome()
=> ReadVariableInt(iAllAxisHomeComplete) > 0;
// ── MOTF ─────────────────────────────────────────
// TODO: 이정인 선임 변수 맵 수신 후 버퍼 번호 및 변수명 교체
bool IScannerACS.MOTFStart()
{
if (IsSimul) return true;
if (RunBuffer((ProgramBuffer)eBuffer.MOTF_Start))
{
Log.I("MOTFStart >> Complete");
return true;
}
Log.D("MOTFStart >> Fail");
return false;
}
bool IScannerACS.MOTFStop()
{
StopBuffer((ProgramBuffer)eBuffer.MOTF_Start);
Log.D("MOTFStop >> Complete");
return true;
}
bool IScannerACS.CheckMOTFComp()
{
if (IsSimul) return true;
return ReadVariableInt(iMOTFComplete) > 0;
}
// ── 레이저 ───────────────────────────────────────
bool IScannerACS.SetLaserIOUse(bool on)
{
if (WriteRealVariable(iLaserUse, on ? 1 : 0))
{
Log.D($"SetLaserIOUse {on} >> Complete");
return true;
}
Log.D("SetLaserIOUse >> Fail");
return false;
}
bool IScannerACS.IsLaserOn()
{
if (!IsActivated || !IsConnected) return false;
return ReadVariableInt(IsLaserOn) > 0;
}
// ── 알람 ─────────────────────────────────────────
bool IScannerACS.ResetAlarm()
{
TerminalCommand("FCLEAR ALL");
return WriteVariableInt(AlarmReset, 1);
}
void IScannerACS.AllAxisServoOff()
{
if (!IsActivated || !IsConnected) return;
SetMotorDisableAll();
}
bool IScannerACS.IsActivated() => IsActivated;
}
}

View File

@ -0,0 +1,168 @@
// WeldingCommon/Devices/ACS/ScannerACS.Parameter.cs
// Gun(PrismaticGunACS.Parameter.cs) 기반으로 Scanner용으로 통합
// 26.04.06 NDY
using AuroraACS;
namespace WeldingCommon.Devices.ACS
{
// ── 레이저 출력 타입 ──────────────────────────────────────
// Gun의 MainRecipe.eOutType과 동일
// partial class 밖에 정의 → IScannerACS 인터페이스에서도 참조 가능
public enum eOutType
{
Analog, // V
Interval, // hz
}
public partial class ScannerACS
{
// ── 알람 ──────────────────────────────────────────
// Gun과 동일
const string AlarmReset = "iAlarmreset";
const string HeavyAlarm = "iAlarm";
const string LightAlarm = "iLightAlarm";
const string AlarmCodes = "iPCAlarmNo"; // 10개
// ── 레이저 ───────────────────────────────────────
// Gun과 동일
const string IsLaserOn = "LCI_DEVICE.LaserEnabled";
const string iLaserUse = "iPCset_LASER_USE";
const string iAStageLCType = "iPCset_ALINE_LCTYPE";
// ── 홈 ───────────────────────────────────────────
// Gun과 동일
const string iAllAxisHomeComplete = "iPCget_ALLAXIS_HOMECOMP";
// ── 축 번호 ──────────────────────────────────────
// Gun과 동일
const string iXAxisNumber = "iPCget_LCI_ASMASK_X";
const string iYAxisNumber = "iPCget_LCI_ASMASK_Y";
// ── 레이저 모드 ───────────────────────────────────
// Gun과 동일
const string iALaserShotMode = "iPCset_ALASER_SHOT_MODE";
// ── OneShot ───────────────────────────────────────
// Gun과 동일
const string rOneShotTime = "rPCset_ALINE_SHOT_TIME";
const string rOneShotCoreVoltage = "rPCset_ALINE_SHOT_CORE_VOLTAGE";
const string rOneShotRingVoltage = "rPCset_ALINE_SHOT_RING_VOLTAGE";
const string iOneShotComplete = "iPCget_ALINE_SHOT_COMP";
// ── LineShot ──────────────────────────────────────
// Gun과 동일
const string rLineWeldingCoreVoltage = "rPCset_ALINE_LINE_CORE_VOLTAGE";
const string rLineWeldingRingVoltage = "rPCset_ALINE_LINE_RING_VOLTAGE";
const string rLineWeldingVel = "rPCset_ALINE_LINE_VEL";
const string rLineWeldingAcc = "rPCset_ALINE_LINE_ACC";
const string rLineWeldingLaserOnX = "rPCset_ALINE_LINE_LASERON_X";
const string rLineWeldingLaserOnY = "rPCset_ALINE_LINE_LASERON_Y";
const string rLineWeldingLaserOffX = "rPCset_ALINE_LINE_LASEROFF_X";
const string rLineWeldingLaserOffY = "rPCset_ALINE_LINE_LASEROFF_Y";
const string iLineWeldingComplete = "iPCget_ALINE_LINE_COMP";
// ── Welding Position 1~8 ──────────────────────────
// Gun과 동일
const string rWeldingPosition1PosX = "rPCset_ALINE_WeldingPosition1_X";
const string rWeldingPosition1PosY = "rPCset_ALINE_WeldingPosition1_Y";
const string rWeldingPosition2PosX = "rPCset_ALINE_WeldingPosition2_X";
const string rWeldingPosition2PosY = "rPCset_ALINE_WeldingPosition2_Y";
const string rWeldingPosition3PosX = "rPCset_ALINE_WeldingPosition3_X";
const string rWeldingPosition3PosY = "rPCset_ALINE_WeldingPosition3_Y";
const string rWeldingPosition4PosX = "rPCset_ALINE_WeldingPosition4_X";
const string rWeldingPosition4PosY = "rPCset_ALINE_WeldingPosition4_Y";
const string rWeldingPosition5PosX = "rPCset_ALINE_WeldingPosition5_X";
const string rWeldingPosition5PosY = "rPCset_ALINE_WeldingPosition5_Y";
const string rWeldingPosition6PosX = "rPCset_ALINE_WeldingPosition6_X";
const string rWeldingPosition6PosY = "rPCset_ALINE_WeldingPosition6_Y";
const string rWeldingPosition7PosX = "rPCset_ALINE_WeldingPosition7_X";
const string rWeldingPosition7PosY = "rPCset_ALINE_WeldingPosition7_Y";
const string rWeldingPosition8PosX = "rPCset_ALINE_WeldingPosition8_X";
const string rWeldingPosition8PosY = "rPCset_ALINE_WeldingPosition8_Y";
// ── Welding Arc ───────────────────────────────────
// Gun과 동일
const string rWeldingEdgePointRad1 = "rPCset_ALINE_EdgePointRad1";
const string rWeldingEdgePointRad2 = "rPCset_ALINE_EdgePointRad2";
const string rWeldingEdgePointRad3 = "rPCset_ALINE_EdgePointRad3";
const string rWeldingEdgePointRad4 = "rPCset_ALINE_EdgePointRad4";
// ── Welding Power ─────────────────────────────────
// Gun과 동일
const string rWeldingCorePowerMax = "rPCset_ALINE_WEL_COREPOWER_MAX";
const string rWeldingCorePowerMin = "rPCset_ALINE_WEL_COREPOWER_MIN";
const string rWeldingRingPowerMax = "rPCset_ALINE_WEL_RINGPOWER_MAX";
const string rWeldingRingPowerMin = "rPCset_ALINE_WEL_RINGPOWER_MIN";
// ── Welding Velocity ──────────────────────────────
// Gun과 동일
const string rWeldingAcc = "rPCset_ALINE_WEL_ACC";
const string rWeldingSVel = "rPCset_ALINE_WEL_SVEL";
const string rWeldingCVel = "rPCset_ALINE_WEL_CVEL";
const string iWeldingComplete = "iPCget_ALINE_WEL_COMP";
// ── Welding Position ──────────────────────────────
// Gun과 동일
const string rWeldingStartPosX = "rPCset_ALINE_WEL_ST_POS_X";
const string rWeldingStartPosY = "rPCset_ALINE_WEL_ST_POS_Y";
const string rWeldingEndPosX = "rPCset_ALINE_WEL_EN_POS_X";
const string rWeldingEndPosY = "rPCset_ALINE_WEL_EN_POS_Y";
// ── Laser ON/OFF Position ─────────────────────────
// Gun과 동일
const string rLaserONPosX = "rPCset_ALINE_LASER_ONPOS_X";
const string rLaserONOffset = "rPCset_ALINE_LASER_ON_OFFSET";
const string rLaserOFFPosX = "rPCset_ALINE_LASER_OFFPOS_X";
const string rLaserOFFOffset = "rPCset_ALINE_LASER_OFF_OFFSET";
// ── Ramping ───────────────────────────────────────
// Gun과 동일
const string rRampingONOverlapDist = "rPCset_ALINE_RAMP_ON_OVERLAP_DIS";
const string rRampingONStartPower = "rPCset_ALINE_RAMP_ON_STARTPOWER";
const string rRampingOFFStartPosX = "rPCset_ALINE_RAMP_OFF_STPOS_X";
const string rRampingOFFOverlapDist = "rPCset_ALINE_RAMP_OFF_OVERLAP_DIS";
const string rRampingOFFEndPower = "rPCset_ALINE_RAMP_OFF_ENDPOWER";
// ── MOTF (Mark On The Fly) ────────────────────────
// Scanner 전용 추가 (Gun에 없음)
// TODO: 이정인 선임 ACS 변수 맵 수신 후 실제 변수명으로 교체
const string iMOTFStart = "iPCset_SCANNER_MOTF_START"; // placeholder
const string iMOTFComplete = "iPCget_SCANNER_MOTF_COMP"; // placeholder
const string rMOTFVelocity = "rPCset_SCANNER_MOTF_VEL"; // placeholder
const string rMOTFAcc = "rPCset_SCANNER_MOTF_ACC"; // placeholder
// ── 버퍼 ─────────────────────────────────────────
// Gun과 동일 + MOTF 추가
private enum eBuffer
{
All_Axis_Home = 0,
AxisX1_Home = 1,
AxisX2_Home = 2,
AxisY1_Home = 3,
AxisY2_Home = 4,
AxisZ_Home = 5,
Welding_Line = 11,
Welding_AllRound = 12,
Welding_OneShot = 13,
// TODO: Scanner MOTF 버퍼 번호 확인 후 수정
MOTF_Start = 14, // placeholder
}
#region 변수 맵 받으면 작업 순서
// 1. ScannerACS.Parameter.cs
// → iMOTFStart, iMOTFComplete, rMOTFVelocity, rMOTFAcc placeholder → 실제 변수명 교체
// → eBuffer.MOTF_Start 번호 교체
// 2. ScannerACS.cs
// → 축 번호 검증 (ACSMotor 생성 시 넣은 번호)
// 3. EquipDef.cs
// → MotorIds의 eMotorIdx 번호 검증
// 4. SeqComponentTeachingRun.cs
// → _ACS?.MOTFStart() 주석 해제
// → CheckMOTFComp() 주석 해제
// → CheckDeviceReady() DeviceIds.ACS 주석 해제
// 5. 빌드 후 실제 장비에서 동작 확인
#endregion
}
}

View File

@ -0,0 +1,588 @@
// WeldingCommon/Devices/ACS/ScannerACS.cs
// Gun(PrismaticGunACS.cs) 기반으로 Scanner용으로 통합
// 변경점:
// 1. 네임스페이스/클래스명 변경 (PrismaticGunACS → ScannerACS)
// 2. MOTF 메서드 3개 추가 (MOTFStart/Stop/CheckMOTFComp)
// 3. 모터 취득 try-catch null-safe 적용
// 4. TeachingPosition 체크 로직 단순화 (Scanner는 X2축만 사용)
// 26.04.06 NDY
using Aurora;
using Aurora.Api;
using Aurora.Api.Devices;
using AuroraACS;
using ACS.SPiiPlusNET;
using Hermes;
using System.Windows;
using WeldingCommon.Settings;
using AuroraOCT;
using WeldingCommon.Device.ACS;
namespace WeldingCommon.Devices.ACS
{
// ── 인터페이스 ────────────────────────────────────────────────────────────
// Gun의 IPrismaticGunACS 기반 + MOTF 3개 추가
public interface IScannerACS
{
bool IsActivated();
// 홈
bool AllAxisHomeStart();
bool AllAxisHomeStop();
bool CheckAllAxisHome();
// MOTF (Scanner 전용 추가 - Gun에 없음)
// TODO: 이정인 선임 변수 맵 수신 후 주석 해제하여 사용
bool MOTFStart();
bool MOTFStop();
bool CheckMOTFComp();
// OneShot (Gun과 동일)
bool SetOneShot(int msecShotTime, double secPhzShotPulse1, double secPhzShotPulse2);
bool OneShotStart();
bool OneShotStop();
bool CheckOneShotComp();
// LineShot (Gun과 동일)
bool SetLineShot(double corePower, double ringPower,
double startPosX, double endPosX,
double startPosY, double endPosY,
double velocity, double acc);
bool LineShotStart();
bool LineShotStop();
bool CheckLineShotComp();
// Welding (Gun과 동일)
bool SetWeldingVel(double acc, double svel, double cvel);
bool SetWeldingPos(double startPosX, double startPosY,
double endPosX, double endPosY,
double camOffsetX, double camOffsetY);
bool SetWeldingArc(double point1, double point2, double point3, double point4);
bool SetWeldingType(eOutType type);
bool SetWeldingPulse(double coreMaxPower, double coreMinPower,
double ringMaxPower, double ringMinPower);
bool SetLaserOnOff(double onPosX, double onOffSet,
double offPosX, double offOffSet,
double camOffsetX, double camOffsetY);
bool SetRamppingOffParameter(double pos, double overlap, double endpower,
double camOffsetX, double camOffsetY);
bool SetRamppingOnParameter(double overlap, double startpower);
bool WeldingStart();
bool WeldingStop();
bool CheckWeldingComp();
// Teaching Position (Gun과 동일)
bool SetWeldingTeachingPosition1(double PosX, double PosY, double camOffsetX, double camOffsetY);
bool SetWeldingTeachingPosition2(double PosX, double PosY, double camOffsetX, double camOffsetY);
bool SetWeldingTeachingPosition3(double PosX, double PosY, double camOffsetX, double camOffsetY);
bool SetWeldingTeachingPosition4(double PosX, double PosY, double camOffsetX, double camOffsetY);
bool SetWeldingTeachingPosition5(double PosX, double PosY, double camOffsetX, double camOffsetY);
bool SetWeldingTeachingPosition6(double PosX, double PosY, double camOffsetX, double camOffsetY);
bool SetWeldingTeachingPosition7(double PosX, double PosY, double camOffsetX, double camOffsetY);
bool SetWeldingTeachingPosition8(double PosX, double PosY, double camOffsetX, double camOffsetY);
// 레이저 (Gun과 동일)
bool SetLaserIOUse(bool on);
bool SetLCILserOn(bool on);
bool SetWeldingAxes(int axisXnum, int axisYnum);
bool IsLaserOn();
// 알람 (Gun과 동일)
bool ResetAlarm();
void AllAxisServoOff();
}
// ── 구현 ──────────────────────────────────────────────────────────────────
public partial class ScannerACS : ACSDevice, IScannerACS
{
// Gun과 동일한 필드
private ACSDeviceSetting _setting;
// Scanner는 Stage X (X2) 1축만 사용
// Gun은 X1, X2, Y1, Y2, Z 5축 사용
private ACSMotor MotorStageX; // AxisX2 = Scanner Stage X
public MotionVariable _motorVariable;
public bool HasHeavyAlarm { get; private set; }
public bool HasLightAlarm { get; private set; }
public int[] HaveAlarmCodes { get => _HaveAlarmCodes; }
private int[] _HaveAlarmCodes = new int[11];
public ScannerACS(bool isSimulation, bool useTerminateUMD = false)
: base(isSimulation, useTerminateUMD)
{
_motorVariable = new MotionVariable();
}
protected override int DoActivate(IDeviceConfig config)
{
if (base.DoActivate(config) != E1.OK)
return E2.ABORTED;
var env = Application.Current.AuroraEnv();
// TODO: Scanner용 SettingIds 확정 후 교체
// Gun: _setting = env.SettingEntry[SettingIds.ACS] as ACSDeviceSetting;
// _setting = env.SettingEntry[ScannerSettingIds.ACS] as ACSDeviceSetting;
// Scanner는 Stage X (X2) 1축만 취득
// Gun은 X1, X2, Y1, Y2, Z 전부 취득
// null-safe: 변수 맵 수신 전 예외 방지
// TODO: 이정인 선임 변수 맵 수신 후 축 번호 검증
try { MotorStageX = env.DeviceManager[CommonDeviceIds.AxisX2] as ACSMotor; }
catch { MotorStageX = null; }
SetMonitoringService(UpdateACSParam, 50);
StartMonitoringService();
return E1.OK;
}
private void UpdateACSParam()
{
// Scanner는 Stage X만 모니터링
// Gun은 X1, X2, Y1, Y2, Z 전부 모니터링
if (MotorStageX != null)
{
_motorVariable.X2AxisPosition = MotorStageX.GetActualPos();
_motorVariable.X2InPosition = MotorStageX.IsInpos();
_motorVariable.X2AxisVelocity = MotorStageX.GetFeedbackVelocity();
_motorVariable.X2AxisAcceleration = MotorStageX.GetAcceleration();
_motorVariable.IsHome = MotorStageX.GetServoStatus() == 1;
}
// 알람 상태 갱신 (Gun과 동일)
HasHeavyAlarm = ReadVariableInt(HeavyAlarm) == 1;
HasLightAlarm = ReadVariableInt(LightAlarm) == 1;
ReadVariableIntArrRef(AlarmCodes, ref _HaveAlarmCodes, ProgramBuffer.ACSC_NONE, 0, 10);
}
protected override int DoDeactivate()
{
StopMonitoringService();
return base.DoDeactivate();
}
// ── 홈 (Gun과 동일) ───────────────────────────────────────────────────
bool IScannerACS.AllAxisHomeStart()
{
if (RunBuffer((ProgramBuffer)eBuffer.All_Axis_Home))
{
Log.D("AllAxisHomeStart >> Complete");
return true;
}
Log.D("AllAxisHomeStart >> Fail");
return false;
}
bool IScannerACS.AllAxisHomeStop()
{
StopBuffer((ProgramBuffer)eBuffer.All_Axis_Home);
Log.D("AllAxisHomeStop >> Complete");
return true;
}
bool IScannerACS.CheckAllAxisHome()
=> ReadVariableInt(iAllAxisHomeComplete) > 0;
// ── MOTF (Scanner 전용 추가 - Gun에 없음) ────────────────────────────
// TODO: 이정인 선임 변수 맵 수신 후 버퍼 번호 및 변수명 교체
bool IScannerACS.MOTFStart()
{
if (IsSimul) return true;
if (RunBuffer((ProgramBuffer)eBuffer.MOTF_Start))
{
Log.I("MOTFStart >> Complete");
return true;
}
Log.D("MOTFStart >> Fail");
return false;
}
bool IScannerACS.MOTFStop()
{
StopBuffer((ProgramBuffer)eBuffer.MOTF_Start);
Log.D("MOTFStop >> Complete");
return true;
}
bool IScannerACS.CheckMOTFComp()
{
if (IsSimul) return true;
return ReadVariableInt(iMOTFComplete) > 0;
}
// ── OneShot (Gun과 동일) ──────────────────────────────────────────────
bool IScannerACS.SetOneShot(int msecShotTime, double secPhzShotPulse1, double secPhzShotPulse2)
{
if (WriteRealVariable(rOneShotTime, msecShotTime) &
WriteRealVariable(rOneShotCoreVoltage, secPhzShotPulse1) &
WriteRealVariable(rOneShotRingVoltage, secPhzShotPulse2))
{
Log.D("SetOneShot >> Complete");
return true;
}
Log.D("SetOneShot >> Fail");
return false;
}
bool IScannerACS.OneShotStart()
{
if (RunBuffer((ProgramBuffer)eBuffer.Welding_OneShot))
{
Log.D("OneShotStart >> Complete");
return true;
}
Log.D("OneShotStart >> Fail");
return false;
}
bool IScannerACS.OneShotStop()
{
IScannerACS motion = this;
motion.SetLCILserOn(false);
StopBuffer((ProgramBuffer)eBuffer.Welding_OneShot);
Log.D("OneShotStop >> Complete");
return true;
}
bool IScannerACS.CheckOneShotComp()
=> ReadVariableInt(iOneShotComplete) > 0;
// ── LineShot (Gun과 동일) ─────────────────────────────────────────────
bool IScannerACS.SetLineShot(double corePower, double ringPower,
double startPosX, double endPosX,
double startPosY, double endPosY,
double velocity, double acc)
{
if (WriteRealVariable(rLineWeldingLaserOnX, startPosX) &
WriteRealVariable(rLineWeldingLaserOnY, startPosY) &
WriteRealVariable(rLineWeldingLaserOffX, endPosX) &
WriteRealVariable(rLineWeldingLaserOffY, endPosY) &
WriteRealVariable(rLineWeldingVel, velocity) &
WriteRealVariable(rLineWeldingAcc, acc) &
WriteRealVariable(rLineWeldingCoreVoltage, corePower) &
WriteRealVariable(rLineWeldingRingVoltage, ringPower))
{
Log.D("SetLineShot >> Complete");
return true;
}
Log.D("SetLineShot >> Fail");
return false;
}
bool IScannerACS.LineShotStart()
{
if (RunBuffer((ProgramBuffer)eBuffer.Welding_Line))
{
Log.D("LineShotStart >> Complete");
return true;
}
Log.D("LineShotStart >> Fail");
return false;
}
bool IScannerACS.LineShotStop()
{
IScannerACS motion = this;
motion.SetLCILserOn(false);
StopBuffer((ProgramBuffer)eBuffer.Welding_Line);
Log.D("LineShotStop >> Complete");
return true;
}
bool IScannerACS.CheckLineShotComp()
=> ReadVariableInt(iLineWeldingComplete) > 0;
// ── Welding (Gun과 동일) ──────────────────────────────────────────────
bool IScannerACS.WeldingStart()
{
if (IsSimul) return true;
if (RunBuffer((ProgramBuffer)eBuffer.Welding_AllRound))
{
Log.I("WeldingStart >> Complete");
return true;
}
Log.D("WeldingStart >> Fail");
return false;
}
bool IScannerACS.WeldingStop()
{
IScannerACS motion = this;
motion.SetLCILserOn(false);
StopBuffer((ProgramBuffer)eBuffer.Welding_AllRound);
Log.D("WeldingStop >> Complete");
return true;
}
bool IScannerACS.CheckWeldingComp()
{
if (IsSimul) return true;
return ReadVariableInt(iWeldingComplete) > 0;
}
bool IScannerACS.SetWeldingVel(double acc, double svel, double cvel)
{
if (WriteRealVariable(rWeldingAcc, acc) &
WriteRealVariable(rWeldingSVel, svel) &
WriteRealVariable(rWeldingCVel, cvel))
{
Log.D("SetWeldingVel >> Complete");
return true;
}
Log.D("SetWeldingVel >> Fail");
return false;
}
bool IScannerACS.SetWeldingPos(double startPosX, double startPosY,
double endPosX, double endPosY,
double camOffsetX, double camOffsetY)
{
if (WriteRealVariable(rWeldingStartPosX, startPosX + camOffsetX) &
WriteRealVariable(rWeldingStartPosY, startPosY + camOffsetY) &
WriteRealVariable(rWeldingEndPosX, endPosX + camOffsetX) &
WriteRealVariable(rWeldingEndPosY, endPosY + camOffsetY))
{
Log.D("SetWeldingPos >> Complete");
return true;
}
Log.D("SetWeldingPos >> Fail");
return false;
}
bool IScannerACS.SetWeldingArc(double point1, double point2, double point3, double point4)
{
if (WriteRealVariable(rWeldingEdgePointRad1, point1) &
WriteRealVariable(rWeldingEdgePointRad2, point2) &
WriteRealVariable(rWeldingEdgePointRad3, point3) &
WriteRealVariable(rWeldingEdgePointRad4, point4))
{
Log.D("SetWeldingArc >> Complete");
return true;
}
Log.D("SetWeldingArc >> Fail");
return false;
}
bool IScannerACS.SetWeldingType(eOutType type)
{
if (WriteVariableInt(iALaserShotMode, (int)type))
{
Log.D("SetWeldingType >> Complete");
return true;
}
Log.D("SetWeldingType >> Fail");
return false;
}
bool IScannerACS.SetWeldingPulse(double coreMaxPower, double coreMinPower,
double ringMaxPower, double ringMinPower)
{
if (WriteRealVariable(rWeldingCorePowerMax, coreMaxPower) &
WriteRealVariable(rWeldingCorePowerMin, coreMinPower) &
WriteRealVariable(rWeldingRingPowerMax, ringMaxPower) &
WriteRealVariable(rWeldingRingPowerMin, ringMinPower))
{
Log.D("SetWeldingPulse >> Complete");
return true;
}
Log.D("SetWeldingPulse >> Fail");
return false;
}
bool IScannerACS.SetLaserOnOff(double onPosX, double onOffSet,
double offPosX, double offOffSet,
double camOffsetX, double camOffsetY)
{
if (WriteRealVariable(rLaserONPosX, onPosX + camOffsetX) &
WriteRealVariable(rLaserONOffset, onOffSet) &
WriteRealVariable(rLaserOFFPosX, offPosX + camOffsetX) &
WriteRealVariable(rLaserOFFOffset, offOffSet))
{
Log.D("SetLaserOnOff >> Complete");
return true;
}
Log.D("SetLaserOnOff >> Fail");
return false;
}
bool IScannerACS.SetRamppingOffParameter(double pos, double overlap, double endpower,
double camOffsetX, double camOffsetY)
{
if (WriteRealVariable(rRampingOFFStartPosX, pos + camOffsetX) &
WriteRealVariable(rRampingOFFOverlapDist, overlap) &
WriteRealVariable(rRampingOFFEndPower, endpower))
{
Log.D("SetRamppingOffParameter >> Complete");
return true;
}
Log.D("SetRamppingOffParameter >> Fail");
return false;
}
bool IScannerACS.SetRamppingOnParameter(double overlap, double startpower)
{
if (WriteRealVariable(rRampingONOverlapDist, overlap) &
WriteRealVariable(rRampingONStartPower, startpower))
{
Log.D("SetRamppingOnParameter >> Complete");
return true;
}
Log.D("SetRamppingOnParameter >> Fail");
return false;
}
// ── Teaching Position (Gun과 동일) ────────────────────────────────────
bool IScannerACS.SetWeldingTeachingPosition1(double PosX, double PosY, double camOffsetX, double camOffsetY)
{
if (WriteRealVariable(rWeldingPosition1PosX, PosX + camOffsetX) &
WriteRealVariable(rWeldingPosition1PosY, PosY + camOffsetY))
{
Log.D($"SetWeldingTeachingPosition1 ({PosX}/{PosY}) >> Complete");
return true;
}
Log.D("SetWeldingTeachingPosition1 >> Fail");
return false;
}
bool IScannerACS.SetWeldingTeachingPosition2(double PosX, double PosY, double camOffsetX, double camOffsetY)
{
if (WriteRealVariable(rWeldingPosition2PosX, PosX + camOffsetX) &
WriteRealVariable(rWeldingPosition2PosY, PosY + camOffsetY))
{
Log.D($"SetWeldingTeachingPosition2 ({PosX}/{PosY}) >> Complete");
return true;
}
Log.D("SetWeldingTeachingPosition2 >> Fail");
return false;
}
bool IScannerACS.SetWeldingTeachingPosition3(double PosX, double PosY, double camOffsetX, double camOffsetY)
{
if (WriteRealVariable(rWeldingPosition3PosX, PosX + camOffsetX) &
WriteRealVariable(rWeldingPosition3PosY, PosY + camOffsetY))
{
Log.D($"SetWeldingTeachingPosition3 ({PosX}/{PosY}) >> Complete");
return true;
}
Log.D("SetWeldingTeachingPosition3 >> Fail");
return false;
}
bool IScannerACS.SetWeldingTeachingPosition4(double PosX, double PosY, double camOffsetX, double camOffsetY)
{
if (WriteRealVariable(rWeldingPosition4PosX, PosX + camOffsetX) &
WriteRealVariable(rWeldingPosition4PosY, PosY + camOffsetY))
{
Log.D($"SetWeldingTeachingPosition4 ({PosX}/{PosY}) >> Complete");
return true;
}
Log.D("SetWeldingTeachingPosition4 >> Fail");
return false;
}
bool IScannerACS.SetWeldingTeachingPosition5(double PosX, double PosY, double camOffsetX, double camOffsetY)
{
if (WriteRealVariable(rWeldingPosition5PosX, PosX + camOffsetX) &
WriteRealVariable(rWeldingPosition5PosY, PosY + camOffsetY))
{
Log.D($"SetWeldingTeachingPosition5 ({PosX}/{PosY}) >> Complete");
return true;
}
Log.D("SetWeldingTeachingPosition5 >> Fail");
return false;
}
bool IScannerACS.SetWeldingTeachingPosition6(double PosX, double PosY, double camOffsetX, double camOffsetY)
{
if (WriteRealVariable(rWeldingPosition6PosX, PosX + camOffsetX) &
WriteRealVariable(rWeldingPosition6PosY, PosY + camOffsetY))
{
Log.D($"SetWeldingTeachingPosition6 ({PosX}/{PosY}) >> Complete");
return true;
}
Log.D("SetWeldingTeachingPosition6 >> Fail");
return false;
}
bool IScannerACS.SetWeldingTeachingPosition7(double PosX, double PosY, double camOffsetX, double camOffsetY)
{
if (WriteRealVariable(rWeldingPosition7PosX, PosX + camOffsetX) &
WriteRealVariable(rWeldingPosition7PosY, PosY + camOffsetY))
{
Log.D($"SetWeldingTeachingPosition7 ({PosX}/{PosY}) >> Complete");
return true;
}
Log.D("SetWeldingTeachingPosition7 >> Fail");
return false;
}
bool IScannerACS.SetWeldingTeachingPosition8(double PosX, double PosY, double camOffsetX, double camOffsetY)
{
if (WriteRealVariable(rWeldingPosition8PosX, PosX + camOffsetX) &
WriteRealVariable(rWeldingPosition8PosY, PosY + camOffsetY))
{
Log.D($"SetWeldingTeachingPosition8 ({PosX}/{PosY}) >> Complete");
return true;
}
Log.D("SetWeldingTeachingPosition8 >> Fail");
return false;
}
// ── 레이저 (Gun과 동일) ───────────────────────────────────────────────
bool IScannerACS.SetLaserIOUse(bool on)
{
if (WriteRealVariable(iLaserUse, on ? 1 : 0))
{
Log.D($"SetLaserIOUse {on} >> Complete");
return true;
}
Log.D("SetLaserIOUse >> Fail");
return false;
}
bool IScannerACS.SetLCILserOn(bool on)
{
if (WriteRealVariable(iAStageLCType, on ? 1 : 0))
{
Log.D($"SetLCILserOn {on} >> Complete");
return true;
}
Log.D("SetLCILserOn >> Fail");
return false;
}
bool IScannerACS.SetWeldingAxes(int axisXnum, int axisYnum)
{
if (WriteVariableInt(iXAxisNumber, axisXnum) &
WriteVariableInt(iYAxisNumber, axisYnum))
{
Log.D("SetWeldingAxes >> Complete");
return true;
}
Log.D("SetWeldingAxes >> Fail");
return false;
}
bool IScannerACS.IsLaserOn()
{
if (!IsActivated || !IsConnected) return false;
return ReadVariableInt(IsLaserOn) > 0;
}
// ── 알람 (Gun과 동일) ─────────────────────────────────────────────────
bool IScannerACS.ResetAlarm()
{
TerminalCommand("FCLEAR ALL");
return WriteVariableInt(AlarmReset, 1);
}
void IScannerACS.AllAxisServoOff()
{
if (!IsActivated || !IsConnected) return;
SetMotorDisableAll();
}
bool IScannerACS.IsActivated() => IsActivated;
}
}

View File

@ -126,6 +126,7 @@
#region
public const string AxisX2 = "AxisX2"; // 26.04.06 추가
public const string ACS = "ACS"; // 26.04.06 추가
#endregion
}

View File

@ -0,0 +1,23 @@
<UserControl x:Class="WeldingCommon.Views.ACS.UCMotorAxses"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WeldingCommon.Views.ACS"
d:DataContext="{d:DesignInstance Type=local:UCMotorAxsesViewModel}"
mc:Ignorable="d">
<!-- Gun과 동일한 구조, 네임스페이스만 변경 -->
<Grid Height="120">
<Grid.RowDefinitions>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.3*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Margin="5" Content="All Servo Off"
Command="{Binding CMD_ServoOff}"/>
<Button Grid.Row="1" Margin="5" Content="Init X Axis"
Command="{Binding CMD_InitAll}"/>
<Button Grid.Row="2" Margin="5" Content="Init Stop"
Background="DarkRed" Command="{Binding CMD_InitStop}"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
// WeldingCommon/Views/ACS/UCMotorAxses.xaml.cs
namespace WeldingCommon.Views.ACS
{
public partial class UCMotorAxses : UserControl
{
public UCMotorAxses()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// WeldingCommon/Views/ACS/UCMotorAxsesViewModel.cs
// Gun의 UCMotorAxsesViewModel.cs 기반 → Scanner X축 전용으로 단순화
// 변경점:
// - 네임스페이스: ILSOS_PrismaticGun.Views → WeldingCommon.Views.ACS
// - IPrismaticGunACS → IScannerACS
// - Y/Z 축 제거, X축만 유지
// - SequenceIds.MotorInit → Scanner용 시퀀스로 교체 필요 (TODO)
// 26.04.06 NDY
using Aurora;
using Aurora.Api;
using Aurora.Api.Devices;
using Aurora.Api.Manager;
using Aurora.View;
using AuroraACS;
using System.Windows;
using System.Windows.Input;
using WeldingCommon.Devices.ACS;
namespace WeldingCommon.Views.ACS
{
public class UCMotorAxsesViewModel : NotifyPropertyChangedBase
{
public ICommand CMD_InitAll { get; }
public ICommand CMD_InitStop { get; }
public ICommand CMD_ServoOff { get; }
// Scanner는 X축(Stage X)만 사용
// Gun은 X2, Y1, Y2, Z 전부 사용
public ACSMotor MotorX { get; set; }
private IScannerACS _acs;
public UCMotorAxsesViewModel(IAuroraEnvironment aurora, IDeviceManager devMngr)
{
CMD_InitAll = new DelegateCommand(InitAllAxses);
CMD_InitStop = new DelegateCommand(InitAllStop);
CMD_ServoOff = new DelegateCommand(ServoOff);
// X축만 취득
try { MotorX = devMngr[CommonDeviceIds.AxisX2] as ACSMotor; }
catch { MotorX = null; }
// ACS 디바이스 취득
try { _acs = devMngr[CommonDeviceIds.ACS] as IScannerACS; }
catch { _acs = null; }
}
private void ServoOff(object obj)
{
_acs?.AllAxisServoOff();
}
private void InitAllAxses(object obj)
{
if (MessageBox.Show("X축 초기화를 진행하시겠습니까?",
"Init", MessageBoxButton.YesNo) == MessageBoxResult.No) return;
// TODO: Scanner용 MotorInit 시퀀스 등록 후 교체
// 현재는 ACS 홈 명령 직접 호출
_acs?.AllAxisHomeStart();
}
private void InitAllStop(object obj)
{
_acs?.AllAxisHomeStop();
MotorX?.HomeStop();
}
}
}

View File

@ -0,0 +1,201 @@
<UserControl x:Class="WeldingCommon.Views.ACS.UCStageManual"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WeldingCommon.Views.ACS"
xmlns:usercontrols="clr-namespace:AuroraUIComponent.UserControls;assembly=AuroraUIComponent"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
x:Name="uc"
mc:Ignorable="d"
d:DesignHeight="800" d:DesignWidth="1200">
<i:Interaction.Triggers>
<i:PropertyChangedTrigger Binding="{Binding IsVisible, ElementName=uc}">
<i:InvokeCommandAction Command="{Binding CMD_VisibleChanged}"
CommandParameter="{Binding IsVisible, ElementName=uc}"/>
</i:PropertyChangedTrigger>
</i:Interaction.Triggers>
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="StTBMonitorName" TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="10"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Margin" Value="5 0"/>
</Style>
<Style x:Key="StTBPosition" TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="24"/>
<Setter Property="Padding" Value="5 5 5 7"/>
<Setter Property="Foreground" Value="#A50034"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid IsEnabled="{Binding IsManualStatus}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<!-- ── Stage X Motion (JOG / ABS) ── -->
<GroupBox Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Margin="5">
<GroupBox.Header>
<TextBlock Text="Stage X Motion" FontSize="20" FontWeight="Bold" Foreground="RoyalBlue"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 현재 위치 표시 -->
<Border Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="5">
<Grid>
<TextBlock Style="{StaticResource StTBMonitorName}">X</TextBlock>
<TextBlock Style="{StaticResource StTBPosition}"
Text="{Binding PosX, StringFormat={}{0:0.####}}"/>
</Grid>
</Border>
<!-- 절대/상대 이동 -->
<usercontrols:UCKeyValue Grid.Row="1" KeyText="Target X" Value="{Binding TargetPosX}"
DecimalPlaces="3" Height="32" Unit="mm"
KeyTextFgColor="White" KeyTextWidth="1*" ValueTextWidth="2*"
Margin="5 5 5 0"/>
<Button Grid.Row="2" Margin="5 2 5 5" Content="Move (ABS)"
Command="{Binding CMD_MoveX}"/>
<!-- JOG 버튼 -->
<Grid Grid.Row="3" HorizontalAlignment="Center" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="60"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Width="55" Height="40" Content="X (-)"
PreviewMouseUp="BtnXJog_M_PreviewMouseUp"
PreviewMouseLeftButtonDown="BtnXJog_M_PreviewMouseLeftButtonDown"
MouseLeave="BtnXJog_M_MouseLeave" Margin="2"/>
<Button Grid.Column="1" Width="55" Height="40" Content="X (+)"
PreviewMouseUp="BtnXJog_P_PreviewMouseUp"
PreviewMouseLeftButtonDown="BtnXJog_P_PreviewMouseLeftButtonDown"
MouseLeave="BtnXJog_P_MouseLeave" Margin="2"/>
</Grid>
<!-- 속도 설정 -->
<usercontrols:UCKeyValue Grid.Row="4" KeyText="Jog Speed" Value="{Binding JogSpeed}"
DecimalPlaces="1" Height="32" Unit="mm/s"
KeyTextFgColor="White" KeyTextWidth="1*" ValueTextWidth="2*"
Margin="5 5 5 0"/>
</Grid>
</GroupBox>
<!-- ── Motor Status ── -->
<GroupBox Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Margin="5">
<GroupBox.Header>
<TextBlock Text="Motor Status" FontSize="20" FontWeight="Bold" Foreground="RoyalBlue"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid Grid.Row="0" ColumnWidth="*" SelectionUnit="FullRow"
ItemsSource="{Binding AxisStatus}" AutoGenerateColumns="False"
HeadersVisibility="Column" FontSize="13" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Motor" Binding="{Binding MotorId}"/>
<DataGridTextColumn Header="- Limit" Binding="{Binding LimitMinusPos, StringFormat={}{0:0.000}}"/>
<DataGridTextColumn Header="+ Limit" Binding="{Binding LimitPlusPos, StringFormat={}{0:0.000}}"/>
<DataGridTemplateColumn Header="Alarm">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<usercontrols:UCLedLight LedStatusVal="{Binding AlarmStatus, Mode=OneWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Servo">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<usercontrols:UCLedLight LedStatusVal="{Binding ServoStatus, Mode=OneWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Home">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<usercontrols:UCLedLight LedStatusVal="{Binding HomeStatus, Mode=OneWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Grid.Row="1" Margin="20 5" FontSize="13"
Content="ACS Alarm Reset" Command="{Binding CMD_AlarmReset}"/>
</Grid>
</GroupBox>
<!-- ── Motor Initialize ── -->
<GroupBox Grid.Row="1" Grid.Column="1" Margin="5">
<GroupBox.Header>
<TextBlock Text="Motor Initialize" FontSize="20" FontWeight="Bold" Foreground="RoyalBlue"/>
</GroupBox.Header>
<local:UCMotorAxses DataContext="{Binding AxsesViewModel}"/>
</GroupBox>
<!-- ── OneShot ── -->
<GroupBox Grid.Row="1" Grid.Column="2" Margin="5">
<GroupBox.Header>
<TextBlock Text="One Shot" FontSize="20" FontWeight="Bold" Foreground="RoyalBlue"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<UniformGrid Grid.Row="0" Columns="1">
<usercontrols:UCKeyValue KeyText="Shot Time(ms)" Value="{Binding OneShotTime}"
KeyTextFgColor="Black" KeyTextBgColor="White" KeyTextSize="13"
ValueTextFgColor="#efefef" ValueTextBgColor="#111111" ValueTextSize="13"/>
<usercontrols:UCKeyValue KeyText="Core Power(V)" Value="{Binding OneShotCorePower}"
KeyTextFgColor="Black" KeyTextBgColor="White" KeyTextSize="13"
ValueTextFgColor="#efefef" ValueTextBgColor="#111111" ValueTextSize="13"
DecimalPlaces="3"/>
<usercontrols:UCKeyValue KeyText="Ring Power(V)" Value="{Binding OneShotRingPower}"
KeyTextFgColor="Black" KeyTextBgColor="White" KeyTextSize="13"
ValueTextFgColor="#efefef" ValueTextBgColor="#111111" ValueTextSize="13"
DecimalPlaces="3"/>
</UniformGrid>
<UniformGrid Grid.Row="1" Columns="3" Margin="5 10">
<Button Content="Start" FontSize="13" Background="DarkRed"
Command="{Binding CMD_OneShotStart}" Margin="2"/>
<Button Content="Stop" FontSize="13" Background="DarkSlateGray"
Command="{Binding CMD_OneShotStop}" Margin="2"/>
<usercontrols:UCLedLight LedStatusVal="{Binding IsOneShotComp, Mode=OneWay}"
Width="30" Height="30"/>
</UniformGrid>
</Grid>
</GroupBox>
<!-- ── Move Stop ── -->
<Button Grid.Row="2" Grid.Column="0" Margin="5 5 5 5" FontSize="15"
Background="DarkSlateGray" Content="Move Stop"
Command="{Binding CMD_MoveStop}"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,51 @@
// WeldingCommon/Views/ACS/UCStageManual.xaml.cs
// Gun의 UCStageManual.xaml.cs 기반 → Scanner X축 전용으로 단순화
// 변경점:
// - 네임스페이스: ILSOS_PrismaticGun.Views → WeldingCommon.Views.ACS
// - Y/Z 축 이벤트 제거, X축만 유지
// - CameraView 제거
// 26.04.06 NDY
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Aurora;
using Aurora.Api.Manager;
namespace WeldingCommon.Views.ACS
{
public partial class UCStageManual : UserControl
{
UCStageManualViewModel ViewModel = null;
public UCStageManual()
{
InitializeComponent();
}
public UCStageManual(IAuroraEnvironment env, IDeviceManager devMngr)
{
InitializeComponent();
ViewModel = new UCStageManualViewModel(env, devMngr);
DataContext = ViewModel;
}
// ── X축 조그 이벤트 ────────────────────────────────────────────────────
private void BtnXJog_P_PreviewMouseUp(object sender, MouseButtonEventArgs e)
=> ViewModel?.XJogStop(null);
private void BtnXJog_P_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
=> ViewModel?.XJogPlus(null);
private void BtnXJog_P_MouseLeave(object sender, MouseEventArgs e)
=> ViewModel?.XJogStop(null);
private void BtnXJog_M_PreviewMouseUp(object sender, MouseButtonEventArgs e)
=> ViewModel?.XJogStop(null);
private void BtnXJog_M_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
=> ViewModel?.XJogMinus(null);
private void BtnXJog_M_MouseLeave(object sender, MouseEventArgs e)
=> ViewModel?.XJogStop(null);
}
}

View File

@ -0,0 +1,244 @@
// WeldingCommon/Views/ACS/UCStageManualViewModel.cs
// Gun의 UCStageManualViewModel.cs 기반 → Scanner X축 전용으로 단순화
// 변경점:
// - 네임스페이스: ILSOS_PrismaticGun.Views → WeldingCommon.Views.ACS
// - IPrismaticGunACS → IScannerACS
// - Y/Z 축 제거, X축만 유지
// - TeachingPositionSetting 제거
// - SeqSplitLine, SeqOneShot → IScannerACS 직접 호출로 교체
// - Camera-Laser Offset 제거
// - SequenceManagerForMainWelder 제거
// 26.04.06 NDY
using Aurora;
using Aurora.Api;
using Aurora.Api.Control;
using Aurora.Api.Devices;
using Aurora.Api.Manager;
using Aurora.View;
using AuroraACS;
using AuroraMotor;
using Hermes;
using System;
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using WeldingCommon.Data;
using WeldingCommon.Devices.ACS;
using JogDirection = AuroraACS.JogDirection;
using Speed = AuroraACS.Speed;
namespace WeldingCommon.Views.ACS
{
public class UCStageManualViewModel : NotifyPropertyChangedBase
{
// ── ViewModel 참조 ────────────────────────────────────────────────────
public UCMotorAxsesViewModel AxsesViewModel
{
get => _AxsesViewModel;
set => SetProperty(ref _AxsesViewModel, value);
}
private UCMotorAxsesViewModel _AxsesViewModel;
// ── 커맨드 ────────────────────────────────────────────────────────────
public ICommand CMD_VisibleChanged { get; }
public ICommand CMD_MoveX { get; }
public ICommand CMD_MoveStop { get; }
public ICommand CMD_OneShotStart { get; }
public ICommand CMD_OneShotStop { get; }
public ICommand CMD_AlarmReset { get; }
public ICommand CMD_CopyToClipboard { get; }
// ── 위치 ──────────────────────────────────────────────────────────────
public double TargetPosX { get; set; }
public double PosX { get => _PosX; set => SetProperty(ref _PosX, value); }
private double _PosX;
// ── 조그 속도 ─────────────────────────────────────────────────────────
public double JogSpeed { get; set; } = 10.0;
// ── OneShot 파라미터 ──────────────────────────────────────────────────
public int OneShotTime { get; set; } = 10;
public double OneShotCorePower { get; set; } = 0.0;
public double OneShotRingPower { get; set; } = 0.0;
public bool IsOneShotComp { get => _IsOneShotComp; set => SetProperty(ref _IsOneShotComp, value); }
private bool _IsOneShotComp = true;
// ── 상태 ──────────────────────────────────────────────────────────────
public bool IsManualStatus { get => _IsManualStatus; set => SetProperty(ref _IsManualStatus, value); }
private bool _IsManualStatus = true;
// ── Motor Status ──────────────────────────────────────────────────────
public ObservableCollection<MotorStatus> AxisStatus
{
get => _AxisStatus;
set => SetProperty(ref _AxisStatus, value);
}
private ObservableCollection<MotorStatus> _AxisStatus = new ObservableCollection<MotorStatus>();
// ── 디바이스 ──────────────────────────────────────────────────────────
// Scanner는 X축(Stage X)만 사용
// Gun은 X1, X2, Y1, Y2, Z 전부 사용
private readonly IAuroraEnvironment _env;
private IScannerACS _motion;
private ScannerACS _acs;
private ACSMotor MotorX;
private readonly DispatcherTimer _timer = new DispatcherTimer();
// ── 생성자 ────────────────────────────────────────────────────────────
public UCStageManualViewModel() { }
public UCStageManualViewModel(IAuroraEnvironment env, IDeviceManager devMngr)
{
_env = env;
AxsesViewModel = new UCMotorAxsesViewModel(env, devMngr);
// Scanner는 X축만 취득 (Gun: X1, X2, Y1, Y2, Z)
try { MotorX = devMngr[CommonDeviceIds.AxisX2] as ACSMotor; }
catch { MotorX = null; }
// ACS 디바이스 취득
try { _motion = _acs = devMngr[CommonDeviceIds.ACS] as ScannerACS; }
catch { _motion = _acs = null; }
// Motor Status 초기화
InitMotorStatus();
// 커맨드 등록
CMD_VisibleChanged = new DelegateCommand(VisibleChanged);
CMD_MoveX = new DelegateCommand(MoveX);
CMD_MoveStop = new DelegateCommand(MoveStop);
CMD_OneShotStart = new DelegateCommand(OneShotStart);
CMD_OneShotStop = new DelegateCommand(OneShotStop);
CMD_AlarmReset = new DelegateCommand(AlarmReset);
CMD_CopyToClipboard = new DelegateCommand(OnCopyToClipboard);
// 모니터링 타이머
_timer.Interval = TimeSpan.FromMilliseconds(500);
_timer.Tick += Monitoring;
// 장비 상태 감시
_env.EventObservable.AsObservable(CommonEventIds.EVENT_EQUIPMENT_STATUS_CHANGE)
.Subscribe(x => IsManualStatus = Status.EquipmentStatus == eEqpStatus.Manual);
}
// ── 모니터링 ──────────────────────────────────────────────────────────
private void Monitoring(object sender, EventArgs e)
{
if (_acs == null || !_acs.IsActivated) return;
// X축 위치 갱신
PosX = _acs._motorVariable.X2AxisPosition;
// Motor Status 갱신
UpdateMotorStatus();
}
private void InitMotorStatus()
{
if (MotorX == null) return;
AxisStatus.Add(new MotorStatus { MotorId = MotorX.DeviceId });
}
private void UpdateMotorStatus()
{
if (MotorX == null || AxisStatus.Count == 0) return;
var limit = MotorX.GetPosLimit();
AxisStatus[0].LimitMinusPos = limit.Min;
AxisStatus[0].LimitPlusPos = limit.Max;
AxisStatus[0].AlarmStatus = _acs.HasHeavyAlarm | _acs.HasLightAlarm;
AxisStatus[0].ServoStatus = MotorX.IsServoOn();
AxisStatus[0].HomeStatus = MotorX.IsHomeCompleted();
}
// ── 커맨드 구현 ───────────────────────────────────────────────────────
private void VisibleChanged(object obj)
{
bool visible = (bool)obj;
if (visible) _timer.Start();
else _timer.Stop();
}
private void MoveX(object obj)
{
if (MotorX == null) return;
if (MessageBox.Show($"Stage X축을 {TargetPosX}으로 이동하시겠습니까?",
"Move", MessageBoxButton.YesNo) == MessageBoxResult.No) return;
MotorX.SetTargetPos(TargetPosX, TargetPosType.ABS);
MotorX.SetMoveVelocityImm(Speed.L);
MotorX.Move();
}
private void MoveStop(object obj)
{
MotorX?.DoStopAll();
}
private void OneShotStart(object obj)
{
if (_motion == null) return;
if (MessageBox.Show("OneShot을 하시겠습니까?",
"OneShot", MessageBoxButton.YesNo) == MessageBoxResult.No) return;
_motion.SetOneShot(OneShotTime, OneShotCorePower, OneShotRingPower);
_motion.SetLCILserOn(true);
_motion.OneShotStart();
IsOneShotComp = false;
}
private void OneShotStop(object obj)
{
_motion?.OneShotStop();
IsOneShotComp = true;
}
private void AlarmReset(object obj)
{
_motion?.ResetAlarm();
}
private void OnCopyToClipboard(object obj)
{
var s = obj?.ToString();
if (!string.IsNullOrWhiteSpace(s))
Clipboard.SetText(s);
}
// ── X축 조그 (code-behind에서 호출) ──────────────────────────────────
internal void XJogPlus(object obj)
=> MotorX?.MoveToSWLimit(JogDirection.plus, Speed.L);
internal void XJogMinus(object obj)
=> MotorX?.MoveToSWLimit(JogDirection.minus, Speed.L);
internal void XJogStop(object obj)
=> MotorX?.Stop();
// 같은 파일, 같은 네임스페이스 안에 정의
public class MotorStatus : NotifyPropertyChangedBase
{
public string MotorId { get => _MotorId; set => SetProperty(ref _MotorId, value); }
private string _MotorId;
public double LimitMinusPos { get => _LimitMinusPos; set => SetProperty(ref _LimitMinusPos, value); }
private double _LimitMinusPos;
public double LimitPlusPos { get => _LimitPlusPos; set => SetProperty(ref _LimitPlusPos, value); }
private double _LimitPlusPos;
public bool AlarmStatus { get => _AlarmStatus; set => SetProperty(ref _AlarmStatus, value); }
private bool _AlarmStatus;
public bool ServoStatus { get => _ServoStatus; set => SetProperty(ref _ServoStatus, value); }
private bool _ServoStatus;
public bool HomeStatus { get => _HomeStatus; set => SetProperty(ref _HomeStatus, value); }
private bool _HomeStatus;
}
}
}

View File

@ -210,8 +210,8 @@
<Compile Include="Data\PLCReportData.cs" />
<Compile Include="Data\WeldingData.cs" />
<Compile Include="Devices\ACS\MotionVariable.cs" />
<Compile Include="Devices\ACS\PrismaticGunACS.cs" />
<Compile Include="Devices\ACS\PrismaticGunACS.Parameter.cs" />
<Compile Include="Devices\ACS\ScannerACS.cs" />
<Compile Include="Devices\ACS\ScannerACS.Parameter.cs" />
<Compile Include="Devices\PLC\PLCController.Address.cs" />
<Compile Include="Devices\PLC\PLCController.Config.cs" />
<Compile Include="Devices\PLC\PLCController.Logic.cs" />
@ -321,6 +321,14 @@
<DependentUpon>UCWorkPos.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Main\WorkPos\UCWorkPosViewModel.cs" />
<Compile Include="Views\Manual\ACS\Stage\UCMotorAxses.xaml.cs">
<DependentUpon>UCMotorAxses.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Manual\ACS\Stage\UCMotorAxsesViewModel.cs" />
<Compile Include="Views\Manual\ACS\Stage\UCStageManual.xaml.cs">
<DependentUpon>UCStageManual.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Manual\ACS\Stage\UCStageManualViewModel.cs" />
<Compile Include="Views\Manual\Scanner\UCManualScannerOperation.xaml.cs">
<DependentUpon>UCManualScannerOperation.xaml</DependentUpon>
</Compile>
@ -646,6 +654,14 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\Manual\ACS\Stage\UCMotorAxses.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\Manual\ACS\Stage\UCStageManual.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\Manual\Scanner\UCManualScannerOperation.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>