Files
HC900-Crawler/CLAUDE.md
windpacer 16fc7a2598 Initial commit: HC900 Crawler
Honeywell HC900을 Modbus TCP로 직접 폴링 → gRPC → C# 크롤러 → PostgreSQL.
기존 Experion OPC UA 데이터 경로를 HC900 직접 통신으로 대체.

- industrial-comm/cpp: C++ Modbus 게이트웨이 (gRPC 서버)
- src: C# .NET 8 ASP.NET Core 크롤러 + 웹 UI (3-Layer)
- mcp-server: Python FastMCP (RAG/NL2SQL/P&ID)
- 다중 컨트롤러(N-Controller) 지원

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 20:28:14 +09:00

8.0 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

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

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).

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:

./build/hc900_gateway [host] [register-map-path] [poll_ms]
# defaults: 192.168.0.240, docs/register-map.json, 1000

Log file: /tmp/hc900_gateway.log. gRPC listens on 0.0.0.0:50051.

C# Crawler (build + run)

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

Converts HC Designer CSV exports → docs/register-map.json used by the gateway at startup:

python3 scripts/build_register_map.py \
    --loop-csv docs/SummaryFucntionBlockReport.csv \
    --signal-csv docs/SignalTags.csv \
    --variable-csv docs/Variables.csv \
    -o docs/register-map.json

Load state labels (StatusPoint descriptors from xlsx) into DB:

python3 scripts/load_state_labels.py

Test Utilities

# 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 FICQ3101.PV FICQ3101.SP FICQ3101.MODE
python3 test/read_tags.py --port 5020 FICQ3101.PV   # against simulator
python3 test/read_tags.py --all --limit 50

# pymodbus must be available; it's expected at /tmp/hc900_venv
/tmp/hc900_venv/bin/python3 test/read_tags.py FICQ3101.PV

Architecture

C++ Gateway (industrial-comm/cpp/)

Hc900Gateway (src/gateway.cpp, include/gateway.h) is the core class:

  • Loads register-map.json at startup into registers_ (vector of RegisterEntry) and tag_index_ (name→index map)
  • Spawns a poll thread (PollLoop) that runs ReadAllRegisters() every poll_interval_ms
  • ReadAllRegisters() groups consecutive registers into batches of ≤120 and issues one read_raw() call per batch (~48 batches total for a full HC900 config, ~117 ms round-trip)
  • Cache (cache_, protected by cache_mutex_) stores CachedValue per tag; quality=192=good, quality=0=bad/stale
  • transport_mutex_ serializes all Modbus transport calls between the poll thread and gRPC WriteTag handlers

Key gRPC operations (all implemented in gateway.cpp):

  • ReadTags — reads from cache, sub-millisecond, no Modbus I/O
  • WriteTag — calls Modbus FC16 directly, then updates cache
  • StreamTags — pushes cache snapshot at requested interval
  • ListTags — returns metadata from in-memory register list
  • HealthCheck — reports connection state, poll count, last poll duration

Controller (src/controller.cpp, include/controller.hpp) wraps ITransport with typed read/write methods. ModbusTCP (src/modbus_tcp.cpp, include/modbus_tcp.hpp) implements the transport.

Codec (src/codec.cpp, include/codec.hpp) handles byte/word order for float32, int32, int64, double. HC900 uses VendorFormat::HC900_FLOAT = {BigEndian, HighFirst, Normal} (FP B format per manual).

Proto: The generated files (gen/modbus_gateway.pb.{cc,h} and gen/modbus_gateway.grpc.pb.{cc,h}) are pre-built and committed. The source proto is at proto/modbus_gateway.proto. The C# copy lives at src/Hc900Crawler/Proto/modbus_gateway.proto and is compiled by MSBuild via Grpc.Tools.

C# Crawler (src/Hc900Crawler/) — 3-Layer Architecture

Project layout (ExperionCrawler 패턴 적용):

src/
  Core/               ← Domain entities, interfaces, application services
  Infrastructure/     ← DB (Hc900DbContext), HC900 services, Control, Kb, Mcp, Trend, Docs
  Hc900Crawler/       ← ASP.NET Core web project (Controllers, wwwroot, Program.cs, csproj)
mcp-server/           ← Python MCP server (copied from ExperionCrawler)

BackgroundServices:

  • Hc900RealtimeService (Infrastructure/Hc900/) — gRPC 폴링 → hc900.realtime_table upsert (500 rows/batch). 상태 노출: IsConnected, PollCount, LastPollAt
  • Hc900HistoryService — 60초 주기 realtime_tablehistory_table 스냅샷 (IsConnected 확인 후 실행)
  • Hc900DigitalEventDetectorService — 1초 주기로 realtime_table 변화 감지 → event_history_table 기록

Hc900GatewayClient (Infrastructure/Hc900/) — IHc900GatewayService 구현. gRPC 채널 lazy 생성. GetHealthAsync(), ListTagsAsync(), WriteTagAsync().

Hc900WriteService — FeedforwardSupervisor·FeedforwardController에서 SP 쓰기용.

Value formatting: 상태 레이블 있으면 {N | LABEL | }, 없으면 float/uint16 string.

Web API endpoints (port 5000):

  • GET /api/gateway/health — gRPC HealthCheck
  • GET /api/gateway/tags — ListTags
  • POST /api/gateway/write — WriteTag
  • GET /api/gateway/status — Hc900RealtimeService 상태
  • GET /api/realtime/points — realtime_table
  • POST /api/history/query — history_table 조회
  • POST /api/events/query — event_history_table 조회
  • /api/pid/*, /api/kb/*, /api/ff/*, /api/t2s/*, /api/ollama/* 등 ExperionCrawler 동일

Database (PostgreSQL, schema hc900)

Hc900DbContext (Infrastructure/Database/Hc900DbContext.cs) — HasDefaultSchema("hc900") + Search Path=hc900 연결문자열. InitializeAsync()에서 모든 테이블·뷰·TimeScaleDB 하이퍼테이블 자동 생성.

Table Purpose
hc900_map_master OPC UA tagnamehc900_tag 매핑, Modbus addr, 데이터타입
realtime_table 실시간 값 (tagname, livevalue, timestamp) — upsert on conflict
history_table 60초 이력 스냅샷 (TimeScaleDB hypertable)
event_history_table 디지털 태그 상태 변경 이벤트 (TRIP/ALARM/RUN 등)
tag_metadata 태그 메타 (description, area, sub_area, state0-7 레이블)
pid_equipment, pid_prefix_rules P&ID 추출 데이터
kb_* Knowledge Base (Qdrant RAG)
ff_* Feedforward 제어 설정/감사

Register Map (docs/register-map.json)

JSON file with a top-level registers array. Each entry: tag, addr (0-based Modbus holding register address), count (1=uint16, 2=float32), type, access ("R"/"RW"), description.

Sources:

  • Loops (PID): SummaryFucntionBlockReport.csv → expanded into per-parameter entries using LOOP_PARAM_OFFSETS in build_register_map.py. Loop #N base address = 0x40 + (N-1)*0x100 (loops 124), 0x7840 + (N-25)*0x100 (loops 2532).
  • Signal Tags (read-only): SignalTags.csv, addresses 0x20000x25E4
  • Variables (R/W): Variables.csv, addresses 0x18C00x1A10

Float format is FP_B (IEEE 754 big-endian, wire order bytes 4,3,2,1).

HC900 Hardware

  • Controller: HC900-C70, IP 192.168.0.240, Modbus TCP port 502
  • Maximum simultaneous connections: 10 (R530 uses 1, leaving 9 available)
  • Unit ID: 0x00 (not used in Modbus TCP mode)
  • Float format must be set to FP B on the controller