# ADR-0012 — Modbus 라이브러리 평가 (Phase 4)

상태: **Accepted (2026-05-08, 야간 자율 사이클 7)**

---

## 결정

**NModbus 채택** — Phase 4에서 `ModbusTcpAdapter` 스켈레톤을 NModbus로 swap.
EasyModbusTCP는 **GPL v3 라이선스로 채택 불가** (상용 Pro/Enterprise 배포 시 GPL 감염).
FluentModbus는 보조 후보로 유지 (Unity .NET Standard 2.0 친화).

## 후보 비교 (2026-05-08 시점)

| 라이브러리 | 라이선스 | Latest | NuGet | Unity 호환 | 권장 |
|---|---|---|---|---|---|
| **NModbus** | **MIT** | 3.0.83 | ✅ | .NET Standard, Unity OK | ⭐ **채택** |
| FluentModbus | (확인 필요, MIT 추정) | 5.3.2 | ✅ | .NET Standard 2.0/2.1 | 보조 |
| EasyModbusTCP.NET | **GPL v3** | 5.6.0 | ✅ | OK | ❌ 상용 불가 |
| NModbus4 | MIT (archived) | 2.1.0 | ✅ | OK | ⚠ archived → NModbus로 |

### NModbus 채택 사유

1. **MIT 라이선스** — Pro/Enterprise 상용 배포 안전
2. **활발한 유지보수** — github.com/NModbus/NModbus
3. **TCP/UDP/RTU 모두 지원** — Phase 5 RTU 시리얼 연결도 가능
4. **NuGet 정식 패키지** — Unity Package Manager에 NuGet for Unity (xoofx/UnityNuGet) 통합 가능
5. **NModbus4 후속** — 옛 NModbus4 사용자 마이그레이션 경로 명확

### EasyModbusTCP 거부 사유

- **GPL v3** — factory00을 같은 GPL로 배포해야 함 (감염). Pro 상용 라이선스 모델과 충돌.
- 4원칙 정체성 5조 (Open-Core 비즈니스) 직접 위배.

### FluentModbus 보조 사유

- Modbus class 0/1/2 함수 코드 부분 지원
- .NET Standard 2.0/2.1 Unity 친화도 높음
- NModbus가 어떤 이유로 사용 불가 시 백업

## Phase 4 swap 가이드

```
1. Unity Package Manager → NuGet for Unity 설치 (com.github-glitchenzo.nugetforunity)
2. NuGet UI → "NModbus" 검색 → install
3. Assets/Scripts/Plc/ModbusTcpAdapter.cs 수정:
   - 메모리 backing Dictionary 제거
   - NModbus.IModbusMaster 사용
   - TcpClient + ModbusFactory.CreateMaster
4. 단위 테스트 — Mock 백엔드는 그대로 두고 ModbusTcpAdapter만 swap
5. 실 PLC 장비 통신 테스트 (사용자 GUI 검증 영역)
```

## NModbus API 매핑

| 현재 (스켈레톤) | NModbus 매핑 |
|---|---|
| `WriteBit("C:0", true)` | `master.WriteSingleCoil(unitId, 0, true)` |
| `ReadBit("C:0")` | `master.ReadCoils(unitId, 0, 1)[0]` |
| `WriteWord("HR:0", 1234)` | `master.WriteSingleRegister(unitId, 0, 1234)` |
| `ReadWord("HR:0")` | `master.ReadHoldingRegisters(unitId, 0, 1)[0]` |
| `Connect()` | `TcpClient.Connect(host, port)` + `ModbusFactory.CreateMaster(client)` |
| `Disconnect()` | `client.Close()` |
| `OnBitChanged` 콜백 | NModbus 미지원 — Adapter 안 polling thread로 구현 |

OnBitChanged는 NModbus가 직접 제공 안 함. Adapter 안에서 1초 간격 polling thread 또는 사용자 명시 push 시 wrap 필요. PoC는 polling 100ms 간격으로 구현.

## 사용 사례 매트릭스

| 사용 시나리오 | NModbus | FluentModbus |
|---|---|---|
| Modbus TCP 클라이언트 (마스터) | ✅ | ✅ |
| Modbus TCP 서버 (슬레이브) | ✅ | ✅ |
| Modbus RTU 시리얼 | ✅ | RTU over TCP만 |
| 가벼운 Mock | ✅ Adapter 스켈레톤 그대로 | ✅ |
| Unity Player 빌드 | ✅ (.NET 4.x) | ✅ (.NET Standard 2.0) |

## OPC UA (Phase 5 입력)

OPC UA는 Modbus 다음 단계 (Siemens / Rockwell 환경에서 점점 표준화).
후보: OPCFoundation/UA-.NETStandard (LGPL+RCL), Workstation.UaClient (Apache 2.0).
Phase 5에서 별도 ADR.

## 참고

- [NModbus GitHub](https://github.com/NModbus/NModbus)
- [NModbus NuGet 3.0.83](https://www.nuget.org/packages/NModbus/)
- [FluentModbus GitHub](https://github.com/Apollo3zehn/FluentModbus)
- [EasyModbusTCP.NET GitHub (GPL v3)](https://github.com/rossmann-engineering/EasyModbusTCP.NET)
- [NModbus4 GitHub (archived)](https://github.com/NModbus4/NModbus4)
- ADR-0006 — Phase 3 범위
- ADR-0011 — Phase 4 범위
