docs: 미커밋 분석·작업지시서·작업일지 반영 (260805~260810)
- 분석_bit15_Resume_핸드셰이크_260805.md - 작업지시서_PTOP_PLC핸드셰이크_260805.md - 분석_HeavyAlarm_비트_동작_260810.md - 작업일지 2026_08_06.md
This commit is contained in:
parent
ec46b15bf6
commit
a43a531e00
168
docs/분석_HeavyAlarm_비트_동작_260810.md
Normal file
168
docs/분석_HeavyAlarm_비트_동작_260810.md
Normal file
@ -0,0 +1,168 @@
|
||||
# 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(시퀀스 정지 대상) 두 건은 **확인 필요** 상태로 남김.
|
||||
214
docs/분석_bit15_Resume_핸드셰이크_260805.md
Normal file
214
docs/분석_bit15_Resume_핸드셰이크_260805.md
Normal file
@ -0,0 +1,214 @@
|
||||
# 분석 — bit15 Resume 핸드셰이크 교착
|
||||
|
||||
- **작성일**: 2026-08-05
|
||||
- **근거 로그**: `docs/Aurora_20260805.log` (현장 PC, CP949)
|
||||
- **대상**: `SeqAutoRunPTOP.cs` / `UCMainAutoRunViewModel.cs`
|
||||
- **증상**: [AutoRun Resume]를 눌러도 PLC bit15(`PLC_WELD_READY_REQ`)가 안 내려가고 진행이 멈춤.
|
||||
모니터링에서 PC 15번 비트를 수동으로 살리면 진행됨
|
||||
|
||||
---
|
||||
|
||||
## 1. 근본 원인 — Resume 스킵 분기가 죽어 있다 ★
|
||||
|
||||
`Builder()`에 Resume 시 핸드셰이크를 건너뛰는 경로가 **선언되어 있다**:
|
||||
|
||||
```csharp
|
||||
// SeqAutoRunPTOP.cs:224
|
||||
sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ]);
|
||||
// 260621: Resume 시 핸드셰이크 스킵
|
||||
```
|
||||
|
||||
그런데 **그 분기를 선택하는 코드가 없다**:
|
||||
|
||||
```csharp
|
||||
// SeqAutoRunPTOP.cs:322-337 CheckInitCondition (Decision 노드)
|
||||
private string CheckInitCondition(...)
|
||||
{
|
||||
Log.I($"[CheckInitCondition] 초기 조건 확인 중");
|
||||
Status.SetWorkingPosition(eWorkingPosition.CHECKING_CONDITION);
|
||||
Thread.Sleep(5);
|
||||
Log.I($"[CheckInitCondition] 조건 확인 완료 → CheckPageNo 이동");
|
||||
return SEQ_CHECK_PAGENO; // ← 무조건. ResumeMode 를 보지 않는다
|
||||
}
|
||||
```
|
||||
|
||||
`ResumeMode` 참조처는 **`CheckCycleStop`(`:883`) 하나뿐**이고, **흐름 분기에는 전혀 쓰이지 않는다.**
|
||||
|
||||
→ 길(`sb.Flows`)은 깔려 있는데 그 길로 가라고 말하는 코드가 없어서,
|
||||
**Resume이든 일반 시작이든 똑같이 `CheckPageNo`로 가서 PLC의 `PAGE_CHANGE_REQ`를 기다린다.**
|
||||
|
||||
### 교착 구조
|
||||
|
||||
```
|
||||
PC : PLC 가 PAGE_CHANGE_REQ(M70508) 줄 때까지 대기 (CheckPageNo)
|
||||
PLC : PC 가 용접 완료(또는 다른 조건) 줄 때까지 bit15 유지
|
||||
→ 서로 기다림
|
||||
```
|
||||
|
||||
- **평소**: PLC가 마침 `PAGE_CHANGE_REQ`를 줌 → 진행 → 정상으로 보였음
|
||||
- **18:36**: PLC가 안 줌 → PC 무한 대기 → TIMEOUT 2회 → Resume 연타
|
||||
|
||||
### 18:36:05 실측 (레시피 31)
|
||||
|
||||
```
|
||||
18:36:05.828 AutoRun Resume Clicked!
|
||||
18:36:05.853 sequence started - 'SeqAutoRunPTOP'
|
||||
18:36:05.854 [Bit15] AutoRun Resume → PC_WELD_READY_ACK ON
|
||||
18:36:05.855 [Sequence_Enter] 레시피 [31], Top_Frame_Welding 활성화
|
||||
18:36:05.866 [CheckPageNo] PLC_PAGE_CHANGE_REQ 신호 대기 중... ← 정체
|
||||
18:37:05.874 W [CheckPageNo] Waiting for PLC_PAGE_CHANGE_REQ -> TIMEOUT...
|
||||
18:38:05.880 W [CheckPageNo] Waiting for PLC_PAGE_CHANGE_REQ -> TIMEOUT...
|
||||
```
|
||||
|
||||
이 상태에서 Resume을 7회 연타 → 전부 `ERROR: sequence is not ready - Running`.
|
||||
|
||||
---
|
||||
|
||||
## 2. 검토했으나 주원인이 아닌 것
|
||||
|
||||
| 가설 | 판정 |
|
||||
|---|---|
|
||||
| PC가 ACK를 안 보냈다 | **아니다.** 클릭 46회 = `PC_WELD_READY_ACK ON` 46회 |
|
||||
| 1초 펄스가 짧아 PLC가 놓친다 | **근거 부족.** 46회 중 45회는 버튼으로 정상 진행 |
|
||||
| 수동/버튼 차이가 레벨 vs 펄스 | **근거 부족.** 위와 같은 이유 |
|
||||
|
||||
> 초기 분석에서 펄스 가설을 원인으로 제시했으나 **철회한다.** 버튼으로 대부분 정상 동작했다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 부수 결함 (교착 상태에서 증상을 악화시킴)
|
||||
|
||||
### 3-1. `ResumeMode`가 안 풀린다
|
||||
|
||||
`CheckCycleStop`(`:883-887`)이 **하루 종일 실행 0회**라, 그 안의 두 줄이 한 번도 안 돌았다:
|
||||
|
||||
```csharp
|
||||
if (ResumeMode) { WriteBit(PC_WELD_READY_ACK, false); ResumeMode = false; }
|
||||
```
|
||||
|
||||
해제 경로가 `OnAutoRunStartRequest:301`(일반 시작)뿐이라, `OnAutoRunResume`의
|
||||
`if (!ResumeMode)` 가드(`:264`)가 무력화 → 연타가 전부 통과.
|
||||
|
||||
### 3-2. `OnAutoRunResume`에 Running 체크 없음 + 거짓 성공 로그
|
||||
|
||||
```csharp
|
||||
:270 WriteBit(PC_WELD_READY_ACK, true); ← ACK 는 나감
|
||||
:283 RunSequenceAutoRun(); ← Start 실패해도 반환값 확인 안 함
|
||||
:284 Log.I("... Auto 재개(정상 핸드셰이크)"); ← 무조건 찍힘
|
||||
```
|
||||
|
||||
`OnAutoRunStartRequest`는 `ApproveAutorunByUserOrAuto():411`에서 Running을 체크하는데 여기엔 없다.
|
||||
|
||||
### 3-3. 타이머 중첩으로 ACK 펄스 축소
|
||||
|
||||
클릭마다 새 `DispatcherTimer` 생성, 기존 취소 없음(`:272-279`).
|
||||
18:38:24~26 실측 — 의도 1초가 **161ms**로 축소. **연타할 때만 발생**하므로 부차적.
|
||||
|
||||
### 3-4. 로그 태그 함정
|
||||
|
||||
`[ExecuteWelding] 각도 offset 적용…`은 ExecuteWelding 노드가 아니다.
|
||||
`SetUploadWeldingList_SeqParams:651`의 `LaserAngleOffsetSelector.Select(…, "ExecuteWelding")`
|
||||
호출자 태그 하드코딩. 이 로그로 "용접까지 갔다"고 읽으면 안 된다 (`rules.md` #1).
|
||||
|
||||
---
|
||||
|
||||
## 4. 로그 통계 (하루 전체)
|
||||
|
||||
| 패턴 | 건수 |
|
||||
|---|---|
|
||||
| `AutoRun Resume Clicked!` / `PC_WELD_READY_ACK ON` / `OFF` | 46 / 46 / 46 |
|
||||
| `GetUploadWeldingList_SeqResult` | 82 |
|
||||
| `WeldingStartReadBit` · `WaitWeldingStart` · `WriteWeldComplete` · `CheckCycleStop` | **전부 0** |
|
||||
| `OnStopped` | 84 |
|
||||
| `ERROR: sequence is not ready - Running` | 9 |
|
||||
|
||||
**모든 사이클이 「업로드 완료 → 즉시 OnStopped」로 끝났다.**
|
||||
AutoRun 시퀀스가 용접 단계에 진입한 적이 없고, 출사는 전부 Manual 수동이었다.
|
||||
|
||||
`[CheckPageNoComp] List` 30건은 전부 3값 일치(`1/1/1`, `2/2/2`, …) — 페이지 읽기 자체는 안정적.
|
||||
|
||||
### 페이지 정체 구간 (16:56~17:40, 레시피 21)
|
||||
|
||||
`List 1/1/1`이 16:56:50 / 17:04:31 / 17:23:05 / 17:34:49 — **44분간 페이지 1을 4회 반복**.
|
||||
17:40:21 Resume(ACK) 직후 `List 2/2/2`로 진행. 이후 페이지마다 Resume 필요.
|
||||
|
||||
---
|
||||
|
||||
## 5. PLC 제어팀 질문 목록 (2026-08-06)
|
||||
|
||||
### A. Resume 시나리오 — 최우선
|
||||
|
||||
1. **작업자가 수동 가접을 끝내고 Resume 하면, PLC는 그다음에 무엇을 줍니까?**
|
||||
- `PAGE_CHANGE_REQ`(M70508)를 **다시 줍니까?** 아니면 이미 준 것으로 간주합니까?
|
||||
- → PC는 지금 **무조건 `PAGE_CHANGE_REQ`를 기다립니다.** 안 주시면 교착입니다.
|
||||
2. **Resume 때 PC가 핸드셰이크(Page/Recipe 확인)를 건너뛰고 바로 용접해도 됩니까?**
|
||||
- → 260621 설계 의도가 이것인데 **코드가 빠져 있습니다.** 이대로 살릴지 결정 필요.
|
||||
3. **`M70515`(bit15)를 내리는 조건 전부**를 알려주십시오. ACK 수신만입니까?
|
||||
- → 근거: 18:36:16에 977ms 정상 ACK 펄스를 받고도 유지됐습니다.
|
||||
4. bit15가 ON인 동안 `PAGE_CHANGE_REQ`를 **안 주는 게 맞습니까?**
|
||||
|
||||
### B. 용접 트리거
|
||||
|
||||
5. **`M70516`(PLC_WELD_TRIGGER_REQ)를 실제로 주신 적이 있습니까?**
|
||||
- → PC 로그상 `WeldingStartReadBit`·`WaitWeldingStart`가 **하루 종일 0건**입니다.
|
||||
6. 가접(bit15) 운용 최종 시나리오 — **자동 재개**입니까 **작업자 버튼**입니까?
|
||||
현재는 매 페이지 [AutoRun Resume]를 눌러야 넘어갑니다.
|
||||
|
||||
### C. 페이지 / 워드
|
||||
|
||||
7. **PLC가 페이지 번호(D70501)를 올리는 조건**은 무엇입니까? ACK 수신입니까?
|
||||
- → 근거: 4사이클 페이지 1 고정 → ACK 1회 → 즉시 페이지 2.
|
||||
8. **정지 시 PC측 에코 워드(`PC_PAGE_NO`/`PC_RECIPE_NO`)를 0으로 지워도 됩니까?**
|
||||
현재는 지우지 않고 유지합니다. PLC가 0을 "무효"로 봅니까 "0번"으로 봅니까?
|
||||
9. **`M70008`(PC_PAGE_CHANGE_COMPLETE)를 실제로 감시하십니까?**
|
||||
`M70009`(PC_RECIPE_CHANGE_COMPLETE)는 미사용이 맞습니까?
|
||||
|
||||
### D. ACK 신호 특성 (보조)
|
||||
|
||||
10. `M70015`(ACK)를 **레벨로 보십니까 엣지로 보십니까?** 최소 유지 시간은요?
|
||||
- → 현재 PC는 1초 펄스. 주원인은 아니나 연타 시 161ms까지 줄어듭니다.
|
||||
|
||||
### E. 함께 대조할 주소
|
||||
|
||||
| PC 심볼 | 주소 | 방향 |
|
||||
|---|---|---|
|
||||
| `PLC_WELD_READY_REQ` / `PC_WELD_READY_ACK` | M70515 / M70015 | PLC→PC / PC→PLC |
|
||||
| `PLC_PAGE_CHANGE_REQ` / `PC_PAGE_CHANGE_COMPLETE` | M70508 / M70008 | PLC→PC / PC→PLC |
|
||||
| `PLC_WELD_TRIGGER_REQ` | M70516 | PLC→PC |
|
||||
| `PLC_PAGE_NO` / `PLC_RECIPE_NO` | D70501 / D70502 | PLC→PC |
|
||||
| `PC_PAGE_NO` / `PC_RECIPE_NO` | D70001 / D70002 | PC→PLC |
|
||||
|
||||
> 주소는 `Device/PLC/PLCController.Address.cs` **주석 기준**이며 enum은 offset 표기다.
|
||||
> 실제 디바이스 번호는 **미검증** — 현장 맵과 직접 대조할 것.
|
||||
|
||||
---
|
||||
|
||||
## 6. PC측 수정 후보 (미적용)
|
||||
|
||||
| # | 내용 | PLC 협의 |
|
||||
|---|---|---|
|
||||
| **1** | `CheckInitCondition`이 `ResumeMode`일 때 `SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ` 반환 | **필요** (질문 1·2) |
|
||||
| 2 | `OnAutoRunResume`에 `RunState == Running` 체크 + 실패 시 `Log.E`·토스트 | 불필요 |
|
||||
| 3 | 타이머를 필드 1개로 두고 재클릭 시 `Stop()` 후 재시작 | 불필요 |
|
||||
| 4 | `[ExecuteWelding]` 호출자 태그를 실제 호출부 이름으로 수정 | 불필요 |
|
||||
| 5 | `ResumeMode` 해제 경로 보강 | 질문 6 결과에 따름 |
|
||||
|
||||
**1번이 본질.** `sb.Flows`는 이미 있으므로 Decision 반환값만 추가하면 된다:
|
||||
|
||||
```csharp
|
||||
if (ResumeMode)
|
||||
{
|
||||
Log.I("[CheckInitCondition] ResumeMode → 핸드셰이크 스킵, 업로드로 직행");
|
||||
return SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ;
|
||||
}
|
||||
return SEQ_CHECK_PAGENO;
|
||||
```
|
||||
|
||||
2~4는 PLC 프로토콜을 건드리지 않으므로 선반영 가능.
|
||||
|
||||
---
|
||||
|
||||
## 관련 문서
|
||||
|
||||
- [작업지시서_PTOP_PLC핸드셰이크_260805.md](./작업지시서_PTOP_PLC핸드셰이크_260805.md) — 읽기 검증(err 체크) 작업지시
|
||||
- [harness-ptop.md](./harness-ptop.md) · [../rules.md](../rules.md)
|
||||
165
docs/작업일지/2026_08_06.md
Normal file
165
docs/작업일지/2026_08_06.md
Normal file
@ -0,0 +1,165 @@
|
||||
# 작업일지 — 2026-08-06
|
||||
|
||||
## 1. Recipe/Page 핸드셰이크 — PLC측 프로토콜 확정
|
||||
|
||||
### 사용자 확인 내용 (현장)
|
||||
|
||||
- "PLC측 시퀀스 확인 함. **레시피, 페이지 동시에 바꿈 → PLC 508번 BIT 살림**"
|
||||
- PC측 시퀀스: 8번 BIT 체크 → 레시피 확인 → 레시피 활성화(체크 3번) → 페이지 확인 후 변경
|
||||
→ `PC_PAGE_CHANGE_COMPLETE` 송신
|
||||
- "**근데 PLC측은 페이지랑 레시피 넘버만 같으면 넘어가도록 되어있음**"
|
||||
- "**PLC 제어는 이제 끝났어요.**"
|
||||
- 30초에 대해: "**우선 계속 확인하는거야.** 30초 동안 중간에 계속 체크를 하고 있고,
|
||||
대기하면서 다시 30초 반복해서 체크하는 것 뿐"
|
||||
|
||||
### 이로써 확정된 것
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 게이트 | **워드**(`PC_PAGE_NO`/`PC_RECIPE_NO`). PC 에코가 PLC 값과 같아지면 진행 |
|
||||
| `PC_PAGE_CHANGE_COMPLETE`(M70008) | **PLC 미사용** — 08/04 질문 ★4 해소 |
|
||||
| 확인 주기 | 상시. 30초는 확인 *간격*이 아니라 한 번의 확인 **창**이며 반복 |
|
||||
|
||||
### 코드 주석이 반대로 적혀 있었음
|
||||
|
||||
`SeqAutoRunPTOP.cs` `OnSeqActionStop` 의 주석 ②가
|
||||
*"PLC가 보는 게이트는 워드가 아니라 `PC_PAGE_CHANGE_COMPLETE` 비트"* 라고 적고,
|
||||
**그 전제로 에코 워드 0 초기화를 롤백**해 뒀다. 확정 내용과 정반대.
|
||||
|
||||
---
|
||||
|
||||
## 2. 방침 전환 — "검증"에서 "에코 보장"으로
|
||||
|
||||
사용자 지시:
|
||||
|
||||
> "**어차피 PLC가 검증하니깐, 괜찮을 것 같은데**"
|
||||
> "우리는 **잘 써주는것만 확인하는 방안**으로 가야해. **그래야 멈추는 일 없이 시퀀스가 핑퐁이 되지**"
|
||||
> "PC쪽에서는 페이지랑 레시피 넘버만 **잘 WRITE 해주고 다음 시퀀스로 넘어가면 돼**"
|
||||
|
||||
### 안전 측면 확인 (코드 검증)
|
||||
|
||||
용접 실행은 `PLC_WELD_TRIGGER_REQ`(M70516) ON 대기가 **별도 게이트**다
|
||||
(`SeqAutoRunPTOP.cs:196`, `:705`). 페이지를 오독하면 에코 불일치 → PLC가 트리거를 안 줌
|
||||
→ **용접이 나가지 않는다.** 오독이 잘못된 용접으로 이어지는 경로는 없음.
|
||||
|
||||
### 그래서 문제가 된 것 — PC가 멈추는 지점
|
||||
|
||||
| 위치 | 증상 |
|
||||
|---|---|
|
||||
| `CheckRecipeNoComp:353-365` | 에코 **없이** return 하는 경로 2개(RecipeNo=0 / 미등록) |
|
||||
| `CheckPageNoComp:534-556` | 범위 밖이면 3초 주기로 **에코 없이 무한 대기** |
|
||||
| `CheckRecipeNoComp:408-419` | 타임아웃이 시작시각만 리셋해 탈출 못 함 |
|
||||
| `CheckPageNoComp:501` 이후 | `Stopped` 가드 없어 정지 중에도 미검증 값 에코 |
|
||||
|
||||
### 부수 발견
|
||||
|
||||
- `CheckPageNoComp` 로그는 "3회 연속 검증"이라 하지만 **3개 값을 비교하는 코드가 없었다**
|
||||
(판정은 마지막 값 하나). `rules.md` #1 전형.
|
||||
- `CheckRecipeNoComp` 재폴링의 `count++ < 3`은 실제로 **4회 순회**.
|
||||
- `ReadWord`의 `err` 반환값을 전 구간에서 버리고 있었음. 미연결 시 `(err, 0)` 반환이라
|
||||
통신 이상이 자동으로 "RecipeNo=0 → 전환 생략" 경로에 빠짐.
|
||||
|
||||
---
|
||||
|
||||
## 3. 분석 문서 2건 작성
|
||||
|
||||
- `docs/분석/2026/08/06/RecipePage_에코보장_방침_분석.md`
|
||||
- `docs/분석/2026/08/06/RecipePage_에코수렴루프_설계.md`
|
||||
|
||||
08/04 문서(`AutoRun재기동_Recipe_Page워드_미반영_원인분석.md`)와 상호 링크.
|
||||
30초 재확인 확인 후 앞 문서의 판단 2건을 **정정**:
|
||||
|
||||
| 항목 | 종전 | 정정 |
|
||||
|---|---|---|
|
||||
| 에코 누락 | 영구 교착 | 자동 복구 — PC가 맞추는 즉시 진행 |
|
||||
| 범위 밖 PageNo | 읽은 값 그대로 에코 | **에코하지 않고 재시도가 맞다** |
|
||||
|
||||
---
|
||||
|
||||
## 4. 확정한 설계 결정
|
||||
|
||||
| 항목 | 결정 |
|
||||
|---|---|
|
||||
| `PLC_RECIPE_NO == 0` | 이상값 → 에코하지 않고 재시도 |
|
||||
| 범위 밖 PageNo | 에코하지 않고 재읽기 |
|
||||
| `CheckPageNo` 레벨 → 상승엣지 | **이번 범위 아님** (수렴 루프가 스테일 오독을 자동 복구) |
|
||||
| 수렴 지연 시 | 토스트만 (알람 승격 안 함) |
|
||||
|
||||
### 불변식
|
||||
|
||||
- `CheckRecipeNoComp` — `PC_RECIPE_NO == PLC_RECIPE_NO` **그리고** 활성 레시피 Id == 그 번호
|
||||
- `CheckPageNoComp` — `PC_PAGE_NO == PLC_PAGE_NO` **그리고** `_CurrentPageNo == PC_PAGE_NO - 1`
|
||||
|
||||
> **워드만 맞추면 안 된다.** 내부 상태를 같이 안 고치면 PLC는 통과시키는데 PC는 옛 페이지로 용접한다.
|
||||
> 이 경우는 PLC 게이트로도 안 막힌다(에코가 맞으니 트리거가 나옴).
|
||||
|
||||
---
|
||||
|
||||
## 5. 구현 — 원복 가능 방식으로
|
||||
|
||||
사용자 지시: "**쓰다가 문제 생기면 원복 하면 되니깐**",
|
||||
"원복할 수 있도록 `#region` 등으로 기존 소스를 삭제하지 말고 분리해서 주석 처리"
|
||||
|
||||
**기존 코드 삭제 0건.** 구버전은 `/* */`로 감싸 `#region 260806 구버전 보존`에 남기고,
|
||||
신규는 `#region 260806 신규`로 표시. 마커 `260806`으로 전량 검색 가능.
|
||||
|
||||
| 파일 | region |
|
||||
|---|---|
|
||||
| `Device/PLC/PLCController.Logic.cs` | 신규 — `ReadOutWord(eWordOutput)` (`ReadOutBit` 패턴 준용) |
|
||||
| `Sequences/SeqAutoRunPTOP.cs` | 상수 신규 / `plcPageNoList` 보존 / `CheckRecipeNoComp` 보존·신규 / `CheckPageNoComp` 보존·신규 / `OnSeqActionStop` 주석 정정 |
|
||||
|
||||
### 주요 변경
|
||||
|
||||
- 두 노드를 수렴 루프로 재작성 — 에코 후 되읽어 일치 확인, 어긋나면 재읽기, 맞는 즉시 다음 노드로
|
||||
- Page 3회 수집(600ms)·Recipe 800ms 재폴링 제거 → **정상 흐름 지연 0**
|
||||
- `while` 탈출 후 `context.Stopped` 가드 추가
|
||||
- **시뮬레이션 가드** — `IsSimul`이면 수렴 생략. PLC 워드가 항상 0이라 안 넣으면
|
||||
시뮬에서 AutoRun이 `CheckRecipeNoComp`를 못 빠져나감
|
||||
|
||||
### 계획에서 바꾼 것
|
||||
|
||||
`using Hermes;` 추가 → **완전 한정명 `Hermes.E1.OK`(4곳)로 변경.**
|
||||
이 파일이 이미 다수 네임스페이스와 `static using` 2개를 열고 있어 `Log`·`Status` 같은
|
||||
흔한 이름에서 모호성(CS0104)이 날 위험이 있었음.
|
||||
|
||||
### 구현 중 확인
|
||||
|
||||
`CheckInitCondition`이 **무조건** `SEQ_CHECK_PAGENO`를 반환한다(`:336`).
|
||||
`:224`의 Resume 핸드셰이크 스킵 분기는 **선언만 있고 타는 경로가 없다.**
|
||||
→ 수렴 루프는 Resume 경로에서도 항상 실행되지만 PLC 값을 따라가며 에코를 최신화할 뿐이라
|
||||
보존값을 훼손하지 않음(충돌 없음).
|
||||
|
||||
---
|
||||
|
||||
## 6. 빌드·커밋·배포
|
||||
|
||||
- **빌드 통과** (사용자 확인: "빌드는 돼")
|
||||
- 현장 PC에 파일 덮어쓰기 완료 — "**현장에 파일 덮어쓰기는 했고, 추후 확인해볼게**"
|
||||
- 머지 진행 중이라 부분 커밋이 안 돼(`cannot do a partial commit during a merge`)
|
||||
**머지 마무리 → 코드 커밋** 순으로 진행. 커밋·푸시 완료
|
||||
|
||||
### 커밋에 포함하지 않은 것
|
||||
|
||||
`docs/PtoP 유니셀 용접기 PLC 인터페이스/` 하위 **삭제 13건**(xlsx 6·7z 2·zip 1·txt 3 등)이
|
||||
unstaged로 잡혀 있었음. 이번 작업과 무관해 그대로 둠.
|
||||
|
||||
---
|
||||
|
||||
## 7. 남은 것
|
||||
|
||||
**런타임 검증 미실시.** 우선순위 순:
|
||||
|
||||
1. **시뮬레이션 기동** — `IsSimul` 가드가 실제로 먹는지 (가장 위험)
|
||||
2. **정상 핸드셰이크** — 로그가 `수렴 완료 (1회차)`로 찍히는지
|
||||
3. **정지 동작** — 수렴 루프 중 Stop 시 에코가 안 나가는지
|
||||
4. **범위 밖 PageNo 복구** — 토스트 후 정상값 복귀 시 재시작 없이 이어지는지
|
||||
|
||||
### 미착수 별건
|
||||
|
||||
- `CheckPageNo` 레벨 → 상승엣지 전환 (08/04 조치 A)
|
||||
- bit15 `ResumeMode` 교착 (08/04 조치 C·D)
|
||||
- PLC 질문 ★1(Manual/정지 시 M70508 OFF 여부)·★2·★5 미해소
|
||||
|
||||
### 환경 변화
|
||||
|
||||
Serena MCP 제거됨 → 이 세션에 셸 실행 경로 없음. 빌드·git은 사용자가 직접 실행.
|
||||
213
docs/작업지시서_PTOP_PLC핸드셰이크_260805.md
Normal file
213
docs/작업지시서_PTOP_PLC핸드셰이크_260805.md
Normal file
@ -0,0 +1,213 @@
|
||||
# 작업지시서 — PTOP AutoRun PLC 핸드셰이크 읽기 검증
|
||||
|
||||
- **작성일**: 2026-08-05
|
||||
- **대상**: `Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPTOP.cs`
|
||||
- **증상**: AutoRun 중 간헐적으로 **Recipe No / Page No 를 PLC에서 받고도 제대로 읽어오지 못함**
|
||||
- **상태**: 아래 §3만 소스 반영. §4는 **미반영 — 작업 대기**
|
||||
|
||||
---
|
||||
|
||||
## 1. 배경
|
||||
|
||||
정지(STOP) 시 BIT는 초기화하는데 WORD(Page·Recipe No)는 초기화하지 않는다는 점 때문에
|
||||
WORD 0 초기화를 추가하려 했으나, 분석 결과 **증상과 방향이 반대**임이 확인됐다.
|
||||
|
||||
| | 방향 | 증상과의 관계 |
|
||||
|---|---|---|
|
||||
| WORD 0 초기화 | PC → PLC (쓰기) | **무관** |
|
||||
| 실제 증상 | PLC → PC (읽기) | 여기가 원인 |
|
||||
|
||||
정지 시 WORD를 지울 필요가 없는 이유(코드 근거):
|
||||
|
||||
- 재진입 시 `CheckRecipeNoComp` / `CheckPageNoComp` 가 어차피 워드를 **재에코**한다
|
||||
(`:395`, `:429`, `:563`).
|
||||
- PLC가 보는 핸드셰이크 게이트는 워드가 아니라 **`PC_PAGE_CHANGE_COMPLETE`(0x08) 비트**이고
|
||||
(`PLCController.Address.cs:63`), 그 비트는 정지 시 이미 false로 내려간다 (`:1206`).
|
||||
- bit15 Resume(Manual 수동 출사 후 이어하기)은 직전 Recipe/Page 에코가 **남아 있어야** 한다
|
||||
(`UCMainAutoRunViewModel.cs:226`).
|
||||
|
||||
---
|
||||
|
||||
## 2. 확인된 사실 (코드 근거)
|
||||
|
||||
### 2-1. `ReadWord` 의 에러 코드를 아무도 검사하지 않는다 ★ 최우선
|
||||
|
||||
```csharp
|
||||
// Device/PLC/PLCController.Logic.cs:16
|
||||
public (int err, short data) ReadWord(eWordInput address) => base.ReadWord((ushort)address, ePLCDevice.D);
|
||||
```
|
||||
|
||||
`SeqAutoRunPTOP.cs` 에서 `.err` 참조 **0건**. 전 호출부가 `.data` 만 꺼낸다.
|
||||
|
||||
| 위치 | 호출 |
|
||||
|---|---|
|
||||
| `:349` | `plcRecipeNo = _PLC.ReadWord(PLC_RECIPE_NO).data;` |
|
||||
| `:376` | `plcRecipeNo = _PLC.ReadWord(PLC_RECIPE_NO).data;` (재폴링) |
|
||||
| `:508` | `plcPageNoList.Add(_PLC.ReadWord(PLC_PAGE_NO).data);` |
|
||||
| `:571` | `curPlcPageNo = _PLC.ReadWord(PLC_PAGE_NO).data;` (ACK 검증) |
|
||||
| `:763` | `thetaRaw = _PLC.ReadWord(HEAD_THETA_POSITION).data;` |
|
||||
|
||||
> 프레임워크 자신은 검사한다 — `WeldingCommon/Devices/PLC/PLCController.Logic.cs:30`
|
||||
> `if (readValue.err != E1.OK) { Log.E("RMS Parameter Read Fail"); return E2.CANCELLED; }`
|
||||
> **같은 코드베이스에 확립된 패턴인데 이 시퀀스만 따르지 않는다.**
|
||||
|
||||
`[추론]` 읽기 실패 시 `data` 가 `default(short)` = 0 으로 오는 것으로 보이나 **미검증**
|
||||
(Melsec 드라이버까지 확인 필요). 다만 아래 2-2·2-3은 0이 올 때 증상과 정확히 일치한다.
|
||||
|
||||
### 2-2. 레시피는 1회만 읽고, 실패값 0이 "전환 의사 없음"으로 흡수된다
|
||||
|
||||
```
|
||||
:348 // Recipe No 1회 읽기 (Page Change bit와 함께 래치되어 안정적이라고 가정 → 노이즈 검증 생략)
|
||||
:349 plcRecipeNo = _PLC.ReadWord(PLC_RECIPE_NO).data;
|
||||
:353 if (plcRecipeNo == 0) → "레시피 전환 생략, 현 Recipe 유지"
|
||||
:356 return SequenceAction.Ok(); ← 알람도 정지도 없음, 에코도 안 함
|
||||
```
|
||||
|
||||
**주석의 "가정"이 깨지는 것이 지금 증상이다.** 통신 실패 0과 PLC가 준 0이 구분되지 않아
|
||||
**이전 레시피 그대로 용접**하는 경로가 열려 있다 (`rules.md` #1 — 주석 맹신 금지 사례).
|
||||
|
||||
읽기 순서상 레시피가 **가장 먼저, 지연 없이** 읽힌다:
|
||||
|
||||
```
|
||||
CheckPageNo:457 PLC_PAGE_CHANGE_REQ ON 감지 → 즉시 break
|
||||
↓ 지연 없음
|
||||
CheckRecipeNoComp:349 ReadWord(PLC_RECIPE_NO) ← 1회, 무검증 ★ 가장 취약
|
||||
↓
|
||||
CheckPageNoComp:508 ReadWord(PLC_PAGE_NO) ×3 (200ms 간격)
|
||||
```
|
||||
|
||||
PLC가 REQ 비트를 먼저 올리고 워드를 몇 스캔 뒤에 쓴다면 **레시피만 stale/0** 을 읽는다.
|
||||
페이지는 600ms를 버니 살아남는다 — "레시피 번호를 못 읽어온다"와 부합.
|
||||
|
||||
### 2-3. "3회 연속 검증" 이 실제로는 검증이 아니다
|
||||
|
||||
```
|
||||
:492 Log.I("PLC 페이지 번호 읽기 시작 (3회 연속 검증)")
|
||||
:508 3개를 plcPageNoList 에 수집
|
||||
:551 Log.E($"List {[0]}/{[1]}/{[2]}") ← 로그로 찍기만 함
|
||||
:532 범위 검사는 plcPageNo(= 마지막 값) 하나만
|
||||
```
|
||||
|
||||
**세 값을 서로 비교하는 코드가 없다.** `2, 0, 3` 으로 읽혀도 마지막 `3` 이 범위 안이면 통과한다.
|
||||
즉 반복만 있고 일치 확인이 없어 **반복의 효과를 얻지 못한 채 600ms만 소비**한다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 이번에 반영한 것 (소스 수정 완료)
|
||||
|
||||
### 3-1. `OnSeqActionStop()` — 워드 0 초기화 제거, 정지 모드만 로그
|
||||
|
||||
`SeqAutoRunPTOP.cs` `#region 260805`
|
||||
|
||||
```csharp
|
||||
if (!ResumeMode) Log.I("일반 정지 (ResumeMode=false) → 에코 워드 보존, 0 초기화 안 함 (…)");
|
||||
else Log.I("Resume 정지 (ResumeMode=true, bit15 Manual 전환) → 에코 워드 반드시 보존 (…)");
|
||||
```
|
||||
|
||||
- **PLC 동작 영향 0** — 워드를 쓰지 않는다. 260621 협의 동작 그대로.
|
||||
- 로그 값은 캐시 필드(`plcPageNo`/`plcRecipeNo`)다. 정지(특히 `OnForceError` 경로)에서
|
||||
PLC를 다시 읽지 않기 위함.
|
||||
- 0 초기화가 필요하다고 판단되면 **PLC 담당 확인 후 `!ResumeMode` 쪽에만** `WriteWord` 를 넣는다.
|
||||
|
||||
### 3-2. `CheckPageNoComp` — `return null` 제거, 대기 + 토스트
|
||||
|
||||
```csharp
|
||||
invalidPageWait++;
|
||||
Log.E($"Invalid PageNo received: {plcPageNo} (활성 Recipe 페이지 수: {recipePageCount}) — 대기 {invalidPageWait}회차");
|
||||
if (invalidPageWait == 1 || invalidPageWait % TOAST_EVERY == 0)
|
||||
MessageUtil.ShowErrorToast($"PLC Page No 이상: {plcPageNo}\n모니터링 확인해주세요.");
|
||||
plcPageNoList.Clear(); checknum = 0; Thread.Sleep(3000); continue;
|
||||
```
|
||||
|
||||
- 종전 `return null` 은 **Aurora 시퀀스 중단**이었다
|
||||
(`docs/Aurora에이전트레퍼런스모음/02_patterns/sequence-builder.md:36` — `null 중단`).
|
||||
일시적 오독 한 번에 AutoRun이 죽고 재시작이 필요했다.
|
||||
- 이제 **중단하지 않고 이 노드에서 대기·재수집**한다. 작업자가 토스트를 보고 모니터링에서
|
||||
값을 확인·조치하면 다음 회차에 정상값을 읽어 **AutoRun 재시작 없이 이어간다.**
|
||||
- `recipePageCount` 는 매 회차 활성 Recipe 의 `Model.Layers.Count()` 로 다시 구한다
|
||||
(`:531`). **Recipe마다 페이지 수가 다르고**, 레시피 반영이 늦으면 이 값도 늦게 맞으므로
|
||||
재시도가 그 지연을 흡수한다.
|
||||
- 토스트는 첫 회차 + 이후 10회마다(약 30초 간격). 폭주 방지.
|
||||
- 탈출 경로는 `while (!context.Stopped)` — **Stop 버튼 정상 동작.**
|
||||
|
||||
> ⚠ 상한이 없으므로 조건이 해소되지 않으면 **무기한 대기**한다. 의도된 동작이다
|
||||
> (작업자 개입 전제). 상한이 필요해지면 `invalidPageWait` 로 쉽게 추가할 수 있다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 작업 지시 (미반영 — 대기)
|
||||
|
||||
우선순위 순. **1번만 해도 원인 규명이 된다.**
|
||||
|
||||
### [P1] `ReadWord` err 체크 래퍼 도입
|
||||
|
||||
- `using Hermes;` 추가 (`E1.OK`). 같은 폴더의 `SeqStreamTest.cs` 등이 이미 사용.
|
||||
- 헬퍼 예: `private (bool ok, short data) ReadWordChecked(eWordInput addr, string caller, int retry, int delayMs)`
|
||||
- `err == E1.OK` 확인 → 실패 시 재시도 → 최종 실패면 `ok=false`
|
||||
- **재시도 후 성공한 경우도 `Log.W`** 로 남길 것 — "간헐적 삐끗"이 로그에 잡히는 지점
|
||||
- 적용: §2-1 표의 5개 호출부
|
||||
- **원칙: 시퀀스 흐름은 바꾸지 않는다.** 현장 가동 중이므로 로그·토스트로만 구분한다.
|
||||
|
||||
로그로 갈려야 하는 4가지:
|
||||
|
||||
| 로그 | 의미 |
|
||||
|---|---|
|
||||
| `읽기 재시도 N회 후 성공` | 간헐적 통신 삐끗 ← **현 증상 후보** |
|
||||
| `ReadWord 최종 실패` | 지속 통신 장애 |
|
||||
| `PLC_RECIPE_NO: [0] ※읽기실패값` | 0이 통신 실패였음 |
|
||||
| `PLC_RECIPE_NO: [0]` (경고 없음) | **PLC가 진짜 0을 준 것** |
|
||||
|
||||
### [P2] 레시피 다중 읽기 + 일치 검증
|
||||
|
||||
`CheckRecipeNoComp:349` 를 페이지와 대칭으로. §2-2의 settle race 흡수용.
|
||||
**단, P1 로그로 원인을 확정한 뒤 판단할 것** — 불필요하면 사이클 타임만 늘어난다.
|
||||
|
||||
### [P3] 3회 수집을 실제 일치 검증으로 + 조기 종료
|
||||
|
||||
```csharp
|
||||
// 같은 값 2회 연속 → 안정으로 보고 조기 종료 (정상 시 600ms → 400ms)
|
||||
if (checknum >= 2 && plcPageNoList[checknum-1] == plcPageNoList[checknum-2]) break;
|
||||
// 3회 다 읽었는데 흔들리면 → 마지막 값을 믿지 말고 재수집
|
||||
if (checknum > 2 && plcPageNoList.Distinct().Count() > 1) { 재수집 }
|
||||
```
|
||||
|
||||
⚠ 범위 검사가 `if (checknum > 2)` 블록 **안**에 있으므로(`:532`), 조기 종료를 넣으려면
|
||||
그 검사를 블록 밖으로 빼는 구조 변경이 필요하다.
|
||||
|
||||
### [P4] `ReadBit` err 체크
|
||||
|
||||
`:457`, `:695`, `:902`, `:928`, `:1148`. 전부 대기 루프 안이라 실패해도 다음 폴링에서
|
||||
재시도된다 — **위험도 낮음.** 여력 있을 때.
|
||||
|
||||
---
|
||||
|
||||
## 5. PLC 협의 / 미확인 사항
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| REQ 비트 ↔ 워드 타이밍 | PLC가 `PAGE_CHANGE_REQ`(M70508)와 워드(D70501/D70502)를 **같은 스캔**에 쓰는가, 비트를 먼저 올리는가. 비트가 먼저면 P2 필수, 동시면 P1만으로 충분 |
|
||||
| 읽기 실패 시 반환값 | `[추론]` `data`=0 으로 보이나 미검증. Melsec 드라이버 확인 필요 |
|
||||
| 정지 시 에코 워드 0 초기화 | PLC가 PC측 에코 워드 0을 "무효"로 보는지 "0번"으로 보는지. **현재 미결 → 그래서 3-1에서 쓰지 않음** |
|
||||
| 현장 로그 | `PLC_RECIPE_NO: [0]` / `Invalid PageNo received: 0` 이 남아 있는지. 있으면 §2 분석이 즉시 확정된다 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 검증 방법
|
||||
|
||||
1. **빌드**
|
||||
`msbuild Machines/ILSOS_PrismaticRD_PTOP/ILSOS_PrismaticRD_PTOP.csproj /p:Configuration=Debug /p:Platform=x64 /v:m /nologo`
|
||||
2. **3-1 확인** — AutoRun 정지 후 로그에 `일반 정지 (ResumeMode=false)` 또는
|
||||
`Resume 정지 (ResumeMode=true…)` 중 하나가 찍히는지. PLC 모니터에서 D70001/D70002가
|
||||
**이전 값을 유지**하는지.
|
||||
3. **3-2 확인** — 범위 밖 PageNo 유입 시 AutoRun이 **죽지 않고** 토스트가 뜨는지.
|
||||
모니터링에서 값 조치 후 **재시작 없이 이어지는지.** Stop 버튼이 대기 중에도 듣는지.
|
||||
|
||||
---
|
||||
|
||||
## 관련 문서
|
||||
|
||||
- **bit15 Resume 정체 분석 + PLC 질문 목록**:
|
||||
[분석_bit15_Resume_핸드셰이크_260805.md](./분석_bit15_Resume_핸드셰이크_260805.md)
|
||||
- 하네스: [`harness-ptop.md`](./harness-ptop.md)
|
||||
- 협업 규칙: [`../rules.md`](../rules.md) — 특히 #1(주석 맹신 금지), #2(수정 승인), #15·#16(추론 표기)
|
||||
- Aurora 시퀀스 규칙: `docs/Aurora에이전트레퍼런스모음/02_patterns/sequence-builder.md`
|
||||
Loading…
x
Reference in New Issue
Block a user