손상된 .git 히스토리(missing tree)로 재초기화 후 작업트리 전체 커밋. .claude/ 만 제외(로컬 에이전트 설정). 구 저장소 백업(.git_corrupt_backup/) 포함. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9.1 KiB
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 |
제어 본문 예시
{ "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 런타임
sudo apt-get update && sudo apt-get install -y dotnet-runtime-10.0 # 또는 dotnet-sdk-10.0
3.2 InfluxDB OSS 2.x
# 설치 (공식 저장소)
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 '<PW>' --force
# 쓰기용 토큰 확인
influx auth list
→ 출력된 토큰을 appsettings.json 의 Influx.Token 에 기입.
3.3 Collector 빌드/실행
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
[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
sudo systemctl daemon-reload && sudo systemctl enable --now erv-collector
journalctl -u erv-collector -f # 로그 확인
3.5 Grafana
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 압축 시 연 수백 MB2GB 수준 → 미니PC SSD로 충분. - 1년 retention 이면 자동으로 오래된 데이터 만료.
6.5 WSL2 로 임시 구축 (미니PC 구입 전 검증)
WSL2 는 실제 Linux VM 이라 위 3장 Ubuntu 설치 절차(.NET / InfluxDB / Grafana)가 그대로 동작한다. 검증/개발용으로 충분하다. 단 아래 2가지를 챙겨야 한다.
(1) WSL 안에서 서비스 자동 실행 — systemd 활성화
/etc/wsl.conf (WSL 내부)
[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 측)
[wsl2]
networkingMode=mirrored
→ wsl --shutdown 후 재시작. WSL 이 호스트 IP 를 공유하므로 EW11 은 Windows PC 의 LAN IP : 6001~6003 으로 바로 접속.
방법 B — portproxy (Windows 10 등) 관리자 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 방화벽 인바운드 허용
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)
{
"Influx": { "Url": "http://127.0.0.1:8086", "Org": "herv", "Bucket": "erv", "Token": "<TOKEN>" },
"SampleIntervalSeconds": 10,
"Sites": [
{ "Port": 6001, "Name": "site01" },
{ "Port": 6002, "Name": "site02" },
{ "Port": 6003, "Name": "site03" }
]
}