# ErvCollector — ERV 24시간 수집/저장 + 원격 모니터링·제어 서버 (미니PC) 3개 현장의 EW11(TCP Client)이 보내는 ERV `0xAA STATUS` 프레임을 수신·파싱하여 **InfluxDB** 에 적재(장기 보관)하고, **내장 웹 대시보드 + HTTP API** 로 실시간 모니터링과 **원격 제어**를 제공한다. ``` [현장1 ERV]─RS485─[EW11]─WiFi/인터넷─┐ TCP 6001 [현장2 ERV]─RS485─[EW11]─WiFi/인터넷─┤ TCP 6002 → [미니PC] ErvCollector ┬ InfluxDB(보관) → Grafana(장기분석) [현장3 ERV]─RS485─[EW11]─WiFi/인터넷─┘ TCP 6003 └ HTTP 8080 (웹 대시보드 + 제어 API) ``` **원격 제어 원리**: EW11 은 양방향 투명 브리지이므로, 현장 EW11 이 서버로 열어둔 **동일 TCP 소켓**으로 서버가 `CTRL_*` 프레임을 역방향 전송하면 EW11 → RS-485 → ERV 로 전달된다(추가 회선 불필요). → ERV 펌웨어(UART1)가 `CTRL_*` 수신 처리(PC_ERV_Protocol 2.1)를 구현해야 실제 동작한다. - 프레임 규격: `../PC_ERV_Protocol.md` - 현장 구분: **포트 분리**(6001/6002/6003 = site01/02/03), 펌웨어/프로토콜 변경 불필요. --- ## 1. 저장 정책 (결정 사항) | 항목 | 값 | |---|---| | 저장 해상도 | **10초 주기 + 이산 상태 변화 시 즉시** | | 보관 기간 | **1년** (InfluxDB 버킷 retention) | | 스택 | **InfluxDB OSS 2.x + Grafana** | - 연속값(PM2.5/PM10/VOC/CO2 등)은 10초 주기 샘플. - 이산값(전원/운전모드/풍량/부가모드/후드/프리셋/에러코드 + 각실 댐퍼·공기질등급·LED)이 바뀌면 **즉시 1건** 추가 기록 → 변화 시점 누락 없음. --- ## 1.5 원격 모니터링·제어 (HTTP API) 서버는 `Http.Prefix`(기본 8080)에서 웹 대시보드와 API 를 제공한다. | 메서드 | 경로 | 설명 | |---|---|---| | GET | `/` | 내장 웹 대시보드(`wwwroot/index.html`) — 3현장 모니터링 + 제어 | | GET | `/api/latest` | 현장별 최신 상태 JSON(온라인 여부 + 글로벌 + 각실) | | POST | `/api/control` | 제어. 헤더 `X-Auth-Token`(토큰 설정 시), 본문 JSON | **제어 본문 예시** ```json { "site":"site01", "action":"power", "value":1 } { "site":"site01", "action":"runmode", "value":2 } // 1환기 2자동 3공청 4바이패스 { "site":"site01", "action":"fan", "value":3 } // 0~4 (자동모드 무시) { "site":"site01", "action":"submode", "type":2, "value":1 }// type 1수면 2조리 3회복 { "site":"site01", "action":"hood", "value":1 } { "site":"site01", "action":"preset", "value":2 } // 0 ECO 1 NORMAL 2 TURBO { "site":"site01", "action":"hyst", "pm25":30,"pm10":50,"voc":300,"co2":700 } { "site":"site01", "action":"damper", "room":2, "value":1 }// room 1거실 2~4침실 { "site":"site01", "action":"led", "room":1, "value":7 }// 0~9 ``` → 서버가 해당 현장 EW11 소켓으로 `CTRL_*` 프레임 송신. 응답 `{"ok":true}` (연결 없으면 503). > **보안**: 제어는 민감하므로 `Http.Token` 을 반드시 설정(대시보드 토큰칸/`X-Auth-Token`). > 운영 시 HTTP 포트는 사내망/VPN 으로 제한하고 외부 직노출 금지 권장. > **바인딩**: `Http.Prefix` — Linux 미니PC 는 `http://*:8080/`(전체 인터페이스), > Windows 비관리자 테스트는 `http://localhost:8080/` 만 가능(`+`/`*` 는 관리자 또는 `netsh http add urlacl` 필요). --- ## 2. 데이터 스키마 (InfluxDB measurement) **erv_global** (tag: `site`) `power, run_mode, auto_state, fan_mode, sub_mode, hood, hyst_preset, hyst_pm25, hyst_pm10, hyst_voc, hyst_co2, error_code` (모두 정수) **erv_room** (tag: `site`, `room`=1~4) `damper, pm25, pm10, voc, co2, air_quality, led_dim, load_score, final_volume` (모두 정수) > 코드값 의미(run_mode 1환기/2자동/…, air_quality 1매우나쁨~4좋음 등)는 Grafana **Value mappings** 로 라벨 표시. --- ## 3. 미니PC 설치 (Ubuntu 기준) ### 3.1 .NET 런타임 ```bash sudo apt-get update && sudo apt-get install -y dotnet-runtime-10.0 # 또는 dotnet-sdk-10.0 ``` ### 3.2 InfluxDB OSS 2.x ```bash # 설치 (공식 저장소) curl -s https://repos.influxdata.com/influxdata-archive_compat.key | sudo gpg --dearmor -o /usr/share/keyrings/influxdata.gpg echo "deb [signed-by=/usr/share/keyrings/influxdata.gpg] https://repos.influxdata.com/debian stable main" | sudo tee /etc/apt/sources.list.d/influxdata.list sudo apt-get update && sudo apt-get install -y influxdb2 sudo systemctl enable --now influxdb # 초기 설정 (org=herv, bucket=erv, 보관 1년=8760h) influx setup --org herv --bucket erv --retention 8760h --username admin --password '' --force # 쓰기용 토큰 확인 influx auth list ``` → 출력된 토큰을 `appsettings.json` 의 `Influx.Token` 에 기입. ### 3.3 Collector 빌드/실행 ```bash cd ErvCollector dotnet publish -c Release -o /opt/erv-collector # appsettings.json 의 Influx.Token / Sites 포트 확인 후 /opt/erv-collector/ErvCollector ``` ### 3.4 systemd 등록 (24시간 자동 실행/재시작) `/etc/systemd/system/erv-collector.service` ```ini [Unit] Description=ERV Collector After=network.target influxdb.service [Service] WorkingDirectory=/opt/erv-collector ExecStart=/opt/erv-collector/ErvCollector Restart=always RestartSec=5 [Install] WantedBy=multi-user.target ``` ```bash sudo systemctl daemon-reload && sudo systemctl enable --now erv-collector journalctl -u erv-collector -f # 로그 확인 ``` ### 3.5 Grafana ```bash sudo apt-get install -y grafana && sudo systemctl enable --now grafana-server # 브라우저 http://<미니PC>:3000 (admin/admin) # Data source: InfluxDB → Query language Flux → URL http://127.0.0.1:8086 → org=herv, token, default bucket=erv ``` 대시보드: site별 변수(`site`)로 현장 전환, room별 패널로 PM/CO2/VOC 추이 + 운전모드/에러 상태 표시. --- ## 4. EW11 설정 (현장 3대, IOTService 또는 웹) | 항목 | 값 | |---|---| | Serial | 115200 / 8 / None / 1 | | Protocol | **TCP Client** | | Server Addr | 회사 서버 **공인 IP**(또는 DDNS 도메인) | | Server Port | 현장1→6001, 현장2→6002, 현장3→6003 | | Security | **AES**(16자 키, 서버와 동일) ※ EW11 TLS는 인증서 미검증이라 AES 병행 권장 | | Keep Alive / Reconnect | 활성 | | Buffer Size | ≥ 256 (STATUS 78B 여유) | > 회사측: 라우터에서 6001~6003 → 미니PC로 **포트포워딩**. 가능하면 inbound 를 3현장 공인 IP로 제한. --- ## 5. 용량 메모 - 10초 주기 × 3현장 ≈ 연 ~950만 레코드(이벤트 추가분 포함). InfluxDB 압축 시 연 수백 MB~2GB 수준 → 미니PC SSD로 충분. - 1년 retention 이면 자동으로 오래된 데이터 만료. --- ## 6.5 WSL2 로 임시 구축 (미니PC 구입 전 검증) WSL2 는 실제 Linux VM 이라 위 **3장 Ubuntu 설치 절차(.NET / InfluxDB / Grafana)가 그대로** 동작한다. 검증/개발용으로 충분하다. 단 아래 2가지를 챙겨야 한다. ### (1) WSL 안에서 서비스 자동 실행 — systemd 활성화 `/etc/wsl.conf` (WSL 내부) ```ini [boot] systemd=true ``` → Windows PowerShell 에서 `wsl --shutdown` 후 재시작하면 `systemctl` 로 influxdb/grafana/erv-collector 를 서비스로 띄울 수 있다. ### (2) 외부 EW11 이 WSL 서비스에 접속 — 네트워크 노출 WSL2 는 기본적으로 NAT 라 **LAN 의 EW11 이 WSL 내부 포트에 직접 못 붙는다**. 둘 중 하나로 해결: **방법 A — Mirrored networking (Windows 11 22H2+, 권장)** `%UserProfile%\.wslconfig` (Windows 측) ```ini [wsl2] networkingMode=mirrored ``` → `wsl --shutdown` 후 재시작. WSL 이 호스트 IP 를 공유하므로 EW11 은 **Windows PC 의 LAN IP : 6001~6003** 으로 바로 접속. **방법 B — portproxy (Windows 10 등)** 관리자 PowerShell: ```powershell $wsl = (wsl hostname -I).Trim().Split(" ")[0] # WSL 내부 IP (재부팅 시 변동) foreach ($p in 6001,6002,6003) { netsh interface portproxy add v4tov4 listenport=$p listenaddress=0.0.0.0 connectport=$p connectaddress=$wsl } ``` ### (3) Windows 방화벽 인바운드 허용 ```powershell New-NetFirewallRule -DisplayName "ERV Collector" -Direction Inbound -Protocol TCP -LocalPort 6001-6003 -Action Allow New-NetFirewallRule -DisplayName "Grafana" -Direction Inbound -Protocol TCP -LocalPort 3000 -Action Allow ``` > ⚠️ WSL/PC 는 **검증용**으로 권장. 24시간 양산 운영은 절전/재부팅/업데이트로 끊길 수 있어 전용 미니PC 가 안전하다. > 또한 방법 B 의 WSL 내부 IP 는 재부팅마다 바뀌므로 방법 A(mirrored)가 편하다. ### (4) 순수 로컬 테스트라면 EW11 없이 같은 PC 에서 데모 프레임만 흘려볼 거면 네트워크 설정 불필요 — `127.0.0.1:6001` 로 바로 송신해 검증 가능(앞선 스모크 테스트 방식). --- ## 7. 구성 파일 (`appsettings.json`) ```json { "Influx": { "Url": "http://127.0.0.1:8086", "Org": "herv", "Bucket": "erv", "Token": "" }, "SampleIntervalSeconds": 10, "Sites": [ { "Port": 6001, "Name": "site01" }, { "Port": 6002, "Name": "site02" }, { "Port": 6003, "Name": "site03" } ] } ```