# CLAUDE.md > **๐Ÿ“Œ [`docs/๋ฒ ์ด์ง์•„ํ‚คํ…์ฒ˜-ํƒœ๊ทธ-๋””์ž์ธ-์ „๋ฉด์žฌ์„ค๊ณ„.md`](docs/๋ฒ ์ด์ง์•„ํ‚คํ…์ฒ˜-ํƒœ๊ทธ-๋””์ž์ธ-์ „๋ฉด์žฌ์„ค๊ณ„.md)** ๋ฅผ ๋จผ์ € ์ฝ์„ ๊ฒƒ โ€” ๋ชจ๋“  ์•„ํ‚คํ…์ฒ˜ ๊ฒฐ์ • ์‚ฌํ•ญ์ด ์ •์˜๋˜์–ด ์žˆ์Œ. ## Project Overview This project replaces the Experion OPC UA data path with a direct Modbus TCP connection to the Honeywell HC900 process controller: ``` Before: HC900 โ”€โ”€Modbus TCPโ”€โ”€โ–ถ Experion R530 โ”€โ”€OPC UAโ”€โ”€โ–ถ ExperionCrawler โ”€โ”€โ–ถ PostgreSQL After: HC900 โ”€โ”€Modbus TCPโ”€โ”€โ–ถ C++ Gateway โ”€โ”€gRPCโ”€โ”€โ–ถ HC900Crawler โ”€โ”€โ–ถ PostgreSQL ``` **ํ•ต์‹ฌ ์›์น™:** - ๋‹จ์ผ ์ง„์‹ค ๊ณต๊ธ‰์› = **`docs/Sinam_Tag_all.xlsx`** (ํƒœ๊ทธ๋ช…ยท์ฃผ์†Œยท๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ „๋ถ€ ํฌํ•จ) - **๋ชจ๋“  ํƒœ๊ทธ๋ช…์€ ๋Œ€๋ฌธ์ž** (OPC UA ๊ฐ•์ œ ์†Œ๋ฌธ์ž ๊ทœ์น™ ํ๊ธฐ) - **์ปจํŠธ๋กค๋Ÿฌ๋ณ„ ๋…๋ฆฝ ๋งต** (`register-map-cN.json`) - Loop ๋ธ”๋ก์€ **ํ†ต์งธ๋กœ ์ฝ๊ธฐ** (๊ฐœ๋ณ„ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ธˆ์ง€) - `build_register_map.py` ํ•˜๋‚˜๋กœ **๋งต ์ƒ์„ฑ + ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ ์žฌ** ๋ชจ๋‘ ์ฒ˜๋ฆฌ Four active components: - **`industrial-comm/cpp/`** โ€” C++ gateway: Modbus TCP poller + gRPC server (`hc900_gateway` binary) - **`src/Hc900Crawler/`** โ€” C# .NET 8 ASP.NET Core web app: full monitoring platform (gRPC client + web UI + KB/P&ID/FF) - **`mcp-server/`** โ€” Python FastMCP server (port 5001): RAG, NL2SQL, P&ID processing tools - **`scripts/`** + **`test/`** โ€” Python utilities for register map generation and testing ## Commands ### C++ Gateway (build) gRPC and abseil are pre-installed to `/tmp/grpc_local` and `/tmp/absl_local` (aarch64). ```bash cd industrial-comm/cpp mkdir -p build && cd build cmake .. make -j$(nproc) ``` Produces `build/hc900_gateway` and `build/libcomm_core.so`. Run the gateway: ```bash ./build/hc900_gateway [host] [register-map-path] [poll_ms] # defaults: 192.168.0.240, docs/register-map-c3.json, 1000 ``` Log file: `/tmp/hc900_gateway.log`. gRPC listens on `0.0.0.0:50051`. ### C# Crawler (build + run) ```bash cd src/Hc900Crawler dotnet build dotnet run ``` Configuration via `appsettings.json`: `Hc900.GatewayAddress` (default `http://localhost:50051`), `Hc900.PollIntervalMs`, `ConnectionStrings.DefaultConnection` (PostgreSQL, `Search Path=hc900`). Serves web UI at `http://0.0.0.0:5000`. ### Register Map Generation (๋‹จ์ผ ์Šคํฌ๋ฆฝํŠธ) `docs/Sinam_Tag_all.xlsx` ํ•˜๋‚˜๋กœ ๋ชจ๋“  ์ฒ˜๋ฆฌ: ```bash python3 scripts/build_register_map.py \ --xlsx docs/Sinam_Tag_all.xlsx \ -o docs/register-map-c3.json \ --controller C3 \ [--db-conn "Host=...;Database=hc900;..."] ``` ์ถœ๋ ฅ: - `register-map-cN.json` โ€” ๊ฒŒ์ดํŠธ์›จ์ด์šฉ ๋ ˆ์ง€์Šคํ„ฐ ๋งต - `tag_metadata` upsert (state labels, units, desc, area) **๋” ์ด์ƒ `load_state_labels.py`๋ฅผ ๋ณ„๋„ ์‹คํ–‰ํ•  ํ•„์š” ์—†์Œ.** ### Test Utilities ```bash # Start Modbus TCP simulator (port 5020, loads register-map.json) python3 test/modbus_sim.py # Read tags directly via Modbus TCP python3 test/read_tags.py FICQ-6101.PV FICQ-6101.SP FICQ-6101.MODE python3 test/read_tags.py --port 5020 FICQ-6101.PV # against simulator python3 test/read_tags.py --all --limit 50 ``` ## Architecture **๋ฐ˜๋“œ์‹œ [`docs/๋ฒ ์ด์ง์•„ํ‚คํ…์ฒ˜-ํƒœ๊ทธ-๋””์ž์ธ-์ „๋ฉด์žฌ์„ค๊ณ„.md`](docs/๋ฒ ์ด์ง์•„ํ‚คํ…์ฒ˜-ํƒœ๊ทธ-๋””์ž์ธ-์ „๋ฉด์žฌ์„ค๊ณ„.md) ๋ฅผ ์ฝ์„ ๊ฒƒ.** ์—ฌ๊ธฐ์„œ๋Š” ์š”์•ฝ๋งŒ ๊ธฐ์ˆ . ### C++ Gateway (`industrial-comm/cpp/`) **`Hc900Gateway`** (`src/gateway.cpp`, `include/gateway.h`) is the core class: - Loads `register-map-cN.json` at startup into `registers_` and `tag_index_` - Spawns poll thread (`PollLoop`) โ†’ `ReadAllRegisters()` every `poll_interval_ms` - Groups consecutive registers into batches of **โ‰ค120** (FC03), **Loop ๋ธ”๋ก์€ ํ†ต์งธ๋กœ 192 regs** - Cache (`cache_`, protected by `cache_mutex_`); quality=192=good, quality=0=bad/stale - `transport_mutex_` serializes Modbus calls between poll thread and gRPC `WriteTag` **Key gRPC operations:** - `ReadTags` โ€” from cache, no Modbus I/O - `WriteTag` โ€” FC16 directly, then update cache - `StreamTags` โ€” push cache at interval - `ListTags` โ€” metadata from register list - `HealthCheck` โ€” connection state, poll count, duration **`Controller`** โ†’ wraps `ITransport` with typed read/write. **`ModbusTCP`** โ†’ FC03/FC16. **Codec** โ†’ FP_B format. ### C# Crawler (`src/Hc900Crawler/`) **Project layout:** ``` src/ Core/ โ† Domain entities, interfaces, application services Infrastructure/ โ† DB (Hc900DbContext), HC900 services, Control, Kb, Mcp, Trend, Docs Hc900Crawler/ โ† ASP.NET Core web project ``` **BackgroundServices:** - `Hc900RealtimeService` โ€” gRPC poll โ†’ `realtime_table` upsert (500 rows/batch) - `Hc900HistoryService` โ€” 60s snapshot `realtime_table` โ†’ `history_table` - `Hc900DigitalEventDetectorService` โ€” 1s digital change detect โ†’ `event_history_table` ### Database (PostgreSQL, schema `hc900`) | Table | Purpose | |---|---| | `realtime_table` | ์‹ค์‹œ๊ฐ„ ๊ฐ’ (tagname, livevalue, timestamp) โ€” upsert on conflict | | `history_table` | 60์ดˆ ์ด๋ ฅ ์Šค๋ƒ…์ƒท (TimeScaleDB hypertable) | | `event_history_table` | ๋””์ง€ํ„ธ ํƒœ๊ทธ ์ƒํƒœ ๋ณ€๊ฒฝ ์ด๋ฒคํŠธ (TRIP/ALARM/RUN ๋“ฑ) | | `tag_metadata` | ํƒœ๊ทธ ๋ฉ”ํƒ€ (desc, area, sub_area, state0-7, units) | | `hc900_map_master` | ํ™œ์„ฑ ํƒœ๊ทธ ๊ด€๋ฆฌ (ํ–ฅํ›„ ๋‹จ์ˆœํ™” ์˜ˆ์ •) | ### HC900 Hardware - Controller: HC900-C70, IP `192.168.0.240`, Modbus TCP port 502 - Unit ID: 1, Float format: **FP B**