feat: 레이저 Head Theta 인터락 추가

발진(ExecuteWelding) 직전 PLC Head_Theta_Position(D70510)을 읽어
헤드 각도가 수평/수직 ±허용오차를 둘 다 벗어나면 알람 후 발진 차단.

- 세팅 메뉴 UI: 수평/수직/허용오차/PLC스케일 + 사용 체크박스
  (LaserInterlockSetting, 기본 미사용)
- fail-closed: PLC 미연결/미활성·스케일0 시 차단, 알람 호출 try/catch
- eWordInput.HEAD_THETA_POSITION(=D70510), 알람코드 210620 추가
- 런타임 설정(CSV 주소맵 D70510, AlarmDescriptions.xlsm 210620 행)은 별도 필요

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
to3796zw 2026-06-19 15:01:58 +09:00
parent 3333d3ca47
commit 0e73086b14
7 changed files with 86 additions and 1 deletions

View File

@ -23,5 +23,7 @@ namespace ILSOS_PrismaticRD_PTOP.Alarm
public const int ALARM_DEVICE_IO_FanAlarm4 = 210612;
public const int ALARM_DEVICE_IO_FanAlarm5 = 210613;
public const int ALARM_DEVICE_IO_FanAlarm6 = 210614;
public const int ALARM_LASER_HEAD_THETA_INTERLOCK = 210620;
}
}

View File

@ -91,7 +91,7 @@ namespace ILSOS_PrismaticRD_PTOP.Device
EMPTY_07,
EMPTY_08,
EMPTY_09,
EMPTY_10,
HEAD_THETA_POSITION, // D70510 (Laser1 Head Theta), index 10
EMPTY_11,
EMPTY_12,
EMPTY_13,

View File

@ -29,6 +29,11 @@ namespace ILSOS_PrismaticRD_PTOP
}
internal class SettingIds : CommonSettingIds
{
public const string LaserInterlock = "LaserInterlock";
}
public class StreamMessageNames
{
public const string MESSAGE_NAME_STRING = "MessageNameString";

View File

@ -224,6 +224,7 @@ namespace ILSOS_PrismaticRD_PTOP
base.RegisterSettings(register);
register(CommonSettingIds.UploadDatas, new SettingUploadDataListForPTOP());
register(SettingIds.LaserInterlock, new LaserInterlockSetting());
}
public override void RegisterServices(IServiceRegistry register)

View File

@ -118,6 +118,7 @@
<Compile Include="Sequences\SequenceManager.cs" />
<Compile Include="Sequences\SeqWaitWeldOrChangePage.cs" />
<Compile Include="Settings\SettingUploadDataListForPTOP.cs" />
<Compile Include="Settings\LaserInterlockSetting.cs" />
<Compile Include="Views\Main\UCMainAutoRun.xaml.cs">
<DependentUpon>UCMainAutoRun.xaml</DependentUpon>
</Compile>

View File

@ -18,6 +18,9 @@ using WeldingCommon.Devices;
using WeldingCommon.Devices.Laser;
using WeldingCommon.Sequences;
using WeldingCommon.Utils;
using WeldingCommon.Alarm;
using ILSOS_PrismaticRD_PTOP.Alarm;
using ILSOS_PrismaticRD_PTOP.Settings;
using static AuroraIO.AJINEXTEK.AjinIO.IOModuleInfo;
using static ILSOS_PrismaticRD_PTOP.Device.PLCControllerPTOP;
using static WeldingCommon.Devices.PLCController;
@ -721,6 +724,38 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences
Status.SetWorkingPosition(eWorkingPosition.EXECUTING_WELD); // 현재 시퀸스 위치
// === Head Theta 인터락: 수평/수직(±tol)에서만 발진 허용 (체크박스 사용 시) ===
var thetaSetting = Application.Current.AuroraEnv().SettingEntry[SettingIds.LaserInterlock] as LaserInterlockSetting;
if (thetaSetting != null && thetaSetting.HeadThetaInterlockEnabled)
{
bool blocked = false;
if (_PLC == null || !_PLC.IsActivated)
{
Log.W("[ExecuteWelding] HeadTheta 인터락 - PLC 미연결/미활성 → 발진 차단");
blocked = true; // fail-closed
}
else
{
short thetaRaw = _PLC.ReadWord(eWordInput.HEAD_THETA_POSITION).data;
double scale = thetaSetting.HeadThetaPlcScale != 0 ? thetaSetting.HeadThetaPlcScale : 1; // 0 가드
double thetaDeg = thetaRaw / scale;
bool okH = Math.Abs(thetaDeg - thetaSetting.HeadThetaHorizontal) <= thetaSetting.HeadThetaTolerance;
bool okV = Math.Abs(thetaDeg - thetaSetting.HeadThetaVertical) <= thetaSetting.HeadThetaTolerance;
Log.I($"[ExecuteWelding] HeadTheta raw={thetaRaw}, deg={thetaDeg:F1} (H={thetaSetting.HeadThetaHorizontal}/V={thetaSetting.HeadThetaVertical}/±{thetaSetting.HeadThetaTolerance})");
if (!okH && !okV)
{
Log.W($"[ExecuteWelding] HeadTheta 인터락 → 수평/수직 ±{thetaSetting.HeadThetaTolerance}° 벗어남(deg={thetaDeg:F1}) → 발진 차단");
blocked = true;
}
}
if (blocked)
{
try { AlarmManager.Alarm(AlarmCode_PTOP.ALARM_LASER_HEAD_THETA_INTERLOCK); }
catch (Exception ex) { Log.E($"[ExecuteWelding] HeadTheta 알람 표시 실패(테이블 미등록?): {ex.Message}"); }
return null; // 발진 차단 + 시퀀스 중단
}
}
_IO._iOUnit.WriteDoBit(Convert.ToInt32(ePrismaticDOAddress.CoreLaser_Control), true);
_IO._iOUnit.WriteDoBit(Convert.ToInt32(ePrismaticDOAddress.RingLaser_Control), true);
Log.I($"[ExecuteWelding] Laser Gate ON (Core+Ring)");

View File

@ -0,0 +1,41 @@
using Aurora.Api;
using PropertyTools.DataAnnotations;
namespace ILSOS_PrismaticRD_PTOP.Settings
{
// Laser 발진 인터락 세팅 (세팅 메뉴에 PropertyGrid로 자동 노출)
public class LaserInterlockSetting : ISettingItem
{
// 체크 시 인터락 동작, 해제 시 미동작 (기본 false: 설정·검증 후 사용)
[Browsable(true)]
[ReadOnly(false)]
[Category("Head Theta Interlock")]
[DisplayName("인터락 사용")]
public bool HeadThetaInterlockEnabled { get; set; } = false;
[Browsable(true)]
[ReadOnly(false)]
[Category("Head Theta Interlock")]
[DisplayName("수평 각도(deg)")]
public double HeadThetaHorizontal { get; set; } = 0;
[Browsable(true)]
[ReadOnly(false)]
[Category("Head Theta Interlock")]
[DisplayName("수직 각도(deg)")]
public double HeadThetaVertical { get; set; } = 90;
[Browsable(true)]
[ReadOnly(false)]
[Category("Head Theta Interlock")]
[DisplayName("허용 오차(±deg)")]
public double HeadThetaTolerance { get; set; } = 2.5;
// PLC Head_Theta_Position 스케일. deg×10 가정(미확정) → 실값 확인 후 조정.
[Browsable(true)]
[ReadOnly(false)]
[Category("Head Theta Interlock")]
[DisplayName("PLC 스케일(deg×?)")]
public double HeadThetaPlcScale { get; set; } = 10;
}
}