LGES-OC5-PtoP-Welder/docs/분석_HeavyAlarm_비트_동작_260810.md
NohDoyeon241104 a43a531e00 docs: 미커밋 분석·작업지시서·작업일지 반영 (260805~260810)
- 분석_bit15_Resume_핸드셰이크_260805.md
- 작업지시서_PTOP_PLC핸드셰이크_260805.md
- 분석_HeavyAlarm_비트_동작_260810.md
- 작업일지 2026_08_06.md
2026-08-11 09:46:02 +09:00

169 lines
8.0 KiB
Markdown

# Heavy(중)알람 비트 동작 분석 — 2026-08-10
> 대상: `PC_HEAVY_ALARM_STATUS`(PC→PLC) / `PLC_HEAVY_ALARM_STATUS`(PLC→PC)
> 질문: **어떤 경우에 Heavy Alarm 비트가 켜지고 꺼지는가**
---
## 1. 한 줄 요약
**"지금 떠 있는 알람 목록에 Level H가 하나라도 있으면 ON"** 을 `SeqStatusMonitor` 가
매 주기 그대로 PLC에 덮어쓴다. 이벤트로 set/reset 하는 래칭 방식이 **아니다**.
---
## 2. 유일한 쓰기 지점
`Machines/ILSOS_PrismaticRD_PTOP/Sequences/Monitoring/SeqStatusMonitor.cs:86-87`
```csharp
_plc.WriteBit(eBitOutput.PC_HEAVY_ALARM_STATUS, AlarmManager.HasAlarmLevelHigh());
_plc.WriteBit(eBitOutput.PC_LIGHT_ALARM_STATUS, AlarmManager.HasAlarmLevelLow());
```
| 항목 | 내용 |
|---|---|
| 주기 | `UpdateStatus` 노드마다. 루프는 `LoopFirst → UpdateStatus → UpdateUI → UpdateIO` 이고 각 노드 `NODE_SLEEP = 20ms` → **약 60~80ms 주기** (`SeqStatusMonitor.cs:32, 74-128`) |
| 방식 | **레벨 미러(매번 덮어쓰기)**. 조건이 사라지면 별도 클리어 없이 자동으로 내려간다 |
| 기동 | `EquipmentSetup.cs:106` 에서 `OnStartUp` 때 Start |
| 정지 | `EquipmentSetup.cs:116` 에서 `OnShutDown` 때 Stop |
### 판정 함수 (`AlarmManager.cs:172-190`)
```
if (ApplicationExt.IsSimulation) return false; ← 시뮬은 무조건 false
GetCurrentAlarmCodes() 중 IsAlarmLevelHigh(code)==true 가 하나라도 있으면 true
```
개별 알람마다 비트를 켜는 게 아니라 **활성 알람 전체에 대한 OR 집계**다.
---
## 3. ★ 어떤 알람이 Level H인지는 소스에 없다
`IsAlarmLevelHigh()`(`AlarmManager.cs:194-201`)는
`_AlarmService.GetAlarmDescription(code).Level` 을 본다. 이 `Level` 은 Aurora `AlarmService` 가
런타임에 읽는 **알람 설명 데이터**에서 오며, 저장소에는 그 파일이 없다
(`*larm*.{csv,xlsx,xml,json}` 검색 0건 — `_out_prismatic_rd_ptop/` 이 `.gitignore` 대상).
**"어떤 알람이 중알람이냐"는 코드가 아니라 설정 데이터가 정한다.**
확인하려면 현장 PC의 알람 설명 파일 또는 UI `Alarm > Description` 화면을 봐야 한다.
---
## 4. 상황별 ON / OFF 정리
### 4-1. ON 되는 경우
| # | 상황 | 조건 |
|---|---|---|
| 1 | Level H 알람이 새로 발생 | 리얼 모드 + 활성 알람에 H 1개 이상 → **다음 주기(≈60~80ms)에 ON** |
| 2 | PLC가 중알람 비트를 올림 | `PLC_HEAVY_ALARM_STATUS` ON → `SeqAlarmMonitor.cs:199-201` 이 210005 발생 → 그 코드가 H면 §1로 이어져 PC 비트도 ON (**PLC 중알람 → PC 중알람 미러링**) |
| 3 | 알람 리셋 후 원인이 그대로 | 리셋으로 목록이 비어도 감시 루프가 다음 회차에 같은 알람을 다시 올림 → **곧바로 재점등** |
주요 알람 발생원(발생 자체는 H/L 무관):
`SeqAlarmMonitor` — PLC EMO·Heavy·Light·Door Interlock, Scanner/Laser/LBAD/OCT/Chiller/LeakSensor 상태 ·
`PLCController.LiveStatusUpdate` — 210002 · 각 시퀀스 내부 알람.
### 4-2. OFF 되는 경우
| # | 상황 | 조건 |
|---|---|---|
| 1 | 활성 알람에 H가 하나도 없음 | 목록이 비었거나 전부 Level L → 다음 주기에 OFF |
| 2 | **시뮬레이션 모드** | `AlarmManager.cs:175-176` — 실제 알람이 떠 있어도 **무조건 false** |
| 3 | 정상 종료 | `OnShutDown`(`EquipmentSetup.cs:115-118`) → `SeqStatusMonitor` Stop → PLC `DoDeactivate` 의 `AllBitClear()`(`PLCBase.cs:249-250`)가 출력 비트를 전부 0으로 만든다 |
### 4-3. ⚠ 갱신이 멈춰 **마지막 값이 그대로 남는** 경우
레벨 미러 방식의 함정이다. 아래 상황에서는 ON/OFF 판정 자체가 일어나지 않는다.
| # | 상황 | 결과 |
|---|---|---|
| 1 | `SeqStatusMonitor` 정지·미기동 | 비트가 갱신되지 않고 **직전 값 유지** |
| 2 | PLC 미연결/미활성 | `WriteBit` 이 `E2.NOT_READY`/`NOT_CONNECTED` 로 조용히 반환(`PLCBase.APIs.cs:334-341`). PLC측은 **마지막 값 유지** |
| 3 | 비정상 종료(강제 종료·크래시) | `AllBitClear()` 가 돌지 않아 **PLC에 ON이 남을 수 있음** |
---
## 5. 알람 목록이 비워지는 경로 (= 비트가 내려가는 유일한 방법)
`AlarmManager.Alarm()` 에는 **짝이 되는 `ClearAlarm` 호출이 없다.** 원인이 사라져도 알람은
목록에 남고, 따라서 비트도 계속 ON이다. **반드시 명시적 리셋이 필요하다.**
| 경로 | 흐름 |
|---|---|
| PLC 알람 리셋 | `PLC_ALARM_RESET`(0x1E) 상승엣지 → `SeqStatusMonitor.cs:89-92` 가 `EVENT_RESET_ALL_ALARMS_REQUEST` 발행 → `AlarmManager.cs:72-77` `ClearAllAlarmRequest` → `_AlarmService.ClearAllAlarm()` |
| UI 알람 리셋 | Aurora `SYS_ALARM_RESET_ALL_ALARM` → `AlarmManager.cs:39-41` |
> **리셋해도 원인이 살아 있으면 소용없다.** 감시 루프가 다음 회차에 같은 알람을 다시 올린다(§4-1 #3).
> 먼저 원인(EMO·Door·디바이스 단선 등)을 해소한 뒤 리셋해야 비트가 실제로 내려간다.
---
## 6. 죽어 있는 경로 — 과거 방식의 잔재
`AlarmManager.cs` 에 같은 취지의 코드가 **4곳 전부 주석 처리**되어 있다.
| 위치 | 내용 |
|---|---|
| `:68-69` | `ClearAllAlarm` → `HeavyAlarmStatusAddr, false` |
| `:75-76` | `ClearAllAlarmRequest` → `HeavyAlarmStatusAddr, false` |
| `:99-103` | `Alarm()` → Level H면 true |
| `:134-138` | `ChangeAlarmDescription()` → Level H면 true |
`:94`·`:129` 주석: *"PLC의 중알람의 경우 PC에서 Dialog만 발생하고 Alarm 중알람 Word를 써주지 않는다"*.
알람 발생 시점 직접 쓰기 → 주기 미러 방식으로 갈아탄 흔적이며, **현재 유효한 건 §2 한 곳뿐이다.**
---
## 7. 주소
| 방향 | 심볼 | 오프셋 | 정의 |
|---|---|---|---|
| PC→PLC | `PC_HEAVY_ALARM_STATUS` | `0x05` | `PLCController.Address.cs:60` (PTOP) |
| PLC→PC | `PLC_HEAVY_ALARM_STATUS` | `0x05` | `PLCController.Address.cs:20` (PTOP) |
| PLC→PC | `PLC_ALARM_RESET` | `0x1E` | `PLCController.Address.cs:35` (PTOP) |
[추론] `M70015`/`M70515`(bit15) 대응 관계를 따르면 각각 `M70005` / `M70505`, 리셋은 `M7051E`.
메모리맵 CSV로 확인 필요.
---
## 8. ⚠ 발견 — 중알람이 떠도 자동운전 시퀀스가 정지되지 않는다
Level H 알람의 또 다른 소비처가 시퀀스 정지인데 **대상 시퀀스가 어긋나 있다.**
```
EquipmentSetup.cs:83-85 OnSetAlarm → isHeavy 면 AlarmManager_PTOP.StopSequenceByAlarm(code)
AlarmManager_PTOP.cs:25-27 Level H 면 StopSequence(SequenceIds.SeqAutoRun) ← 대상
```
그러나 실제로 도는 자동운전 시퀀스는 `SeqAutoRunPTOP` 이다.
```
UCMainAutoRunViewModel.cs:338 //_SeqAutoRun.Start(param, OnCompleteAutoRun); ← 주석
UCMainAutoRunViewModel.cs:342 _SeqAutoRunPTOP.Start(param, OnCompleteAutoRunPTOP); ← 실제 기동
UCMainAutoRunViewModel.cs:428 //_SeqAutoRun.Stop(); ← 주석
```
`StopSequence()`(`AlarmManager.cs:164-169`)는 `RunState == Running` 일 때만 `EmergencyStop()` 을
호출한다. `SeqAutoRun`(Prismatic 빌더)은 등록(`EquipmentSetup.cs:211`)만 되고 **한 번도 기동되지
않으므로** 조건에 걸리지 않아 **아무 일도 일어나지 않는다.**
→ 현재는 중알람 시 **PC_HEAVY_ALARM_STATUS 비트는 정상 ON 되지만 PC측 자동운전은 계속 돈다.**
설비가 서는 것은 PLC가 그 비트를 보고 세우기 때문일 가능성이 크다.
[추론] `SeqAutoRunTDC` → `SeqAutoRunPTOP` 전환(260605) 때 `AlarmManager_PTOP` 가 함께 갱신되지 않은
잔재로 보인다. `EquipmentSetup.cs:262` 의 AutoRun 뷰 생성자도 아직 `SequenceIds.SeqAutoRunTDC` 를
넘긴다(다만 VM이 `:100-101` 에서 ID로 직접 해석하므로 무해).
**확인 필요:** PLC가 중알람 시 실제로 설비를 세우는가.
세운다면 이중 안전장치 하나가 죽어 있는 상태이고, 안 세운다면 실제 위험이다.
---
## 9. 상태
- 분석만 수행. **소스 미수정.**
- §3(알람 Level 데이터) · §8(시퀀스 정지 대상) 두 건은 **확인 필요** 상태로 남김.