Files
HC900-Crawler/docs/experion-indexed-address-mapping.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

12 KiB
Raw Permalink Blame History

Experion Indexed Address → HC900 Fixed Modbus Memory Map 변환 공식

대상: HC900-C70 (C4 컨트롤러), Experion HS R530 OPC 서버
출처: C4-All-Modbus-Map.csv, Sinam_Tag_all.xlsx (SourceAddressPV/OP/MD 컬럼), HC900 Communications Manual Rev.13


1. 배경

Experion OPC 서버는 HC900 Modbus 레지스터를 직접 주소 대신 Indexed Address 형식으로 참조한다.
Sinam_Tag_all.xlsxSourceAddressPV / SourceAddressOP / SourceAddressMD 컬럼에 이 형식이 기록된다.

예시:
  C4 LOOP 1 LOOPSTAT    → C4 컨트롤러, PID Loop #1, Loop Status Register
  C4 TAG 32 VALUE       → C4 컨트롤러, Signal Tag #32, 값
  C4 MATH_VAR 5 VALUE   → C4 컨트롤러, Variable #5, 값
  C4 LOOPX 27 LOOPSTAT  → C4 컨트롤러, 확장 Loop #27 (25-32), Loop Status Register

2. Indexed Address 형식

{CTRL} {PARTITION} {N} {PARAM}

CTRL      : 컨트롤러 식별자 (C1, C2, C3, C4)
PARTITION : LOOP | LOOPX | TAG | MATH_VAR
N         : 1-based 인덱스 번호
PARAM     : 파라미터명 (하단 §5 참조)

3. HC900 Fixed Map 파티션별 Modbus 주소 공식

3-1. PID Loop (1-24번) — LOOP

base_addr(N) = 0x0040 + (N - 1) × 0x0100      (N = 1 ~ 24)

Modbus_addr = base_addr(N) + param_offset
Loop# Base Addr HC900 태그명 (C4)
1 0x0040 FIC-9101
2 0x0140 FICQ-9114
3 0x0240 FIC-9113
4 0x0340 FIC-9118
5 0x0440 FIC-9116
6 0x0540 TIC-9111A
7 0x0640 PIC-9111A
8 0x0740 FIC-9201
9 0x0840 FIC-9213
10 0x0940 FIC-9214
11 0x0A40 FIC-9218
12 0x0B40 FIC-9216
13 0x0C40 TIC-9211
14 0x0D40 PIC-9211B
15 0x0E40 LIC-9113
16 0x0F40 LIC-9213
17 0x1040 FICQ-10101
18 0x1140 FICQ-10113
19 0x1240 FICQ-10114A
20 0x1340 FIC-10118
21 0x1440 LICA-10113
22 0x1540 FICQ-10116
23 0x1640 TIC-10111A
24 0x1740 PIC-10111A

3-2. 확장 PID Loop (25-32번) — LOOPX

base_addr(N) = 0x7840 + (N - 25) × 0x0100     (N = 25 ~ 32)

Modbus_addr = base_addr(N) + param_offset
Loop# Base Addr HC900 태그명 (C4)
25 0x7840 FICQ-10201
26 0x7940 FICQ-10213
27 0x7A40 FICQ-10214
28 0x7B40 FICQ-10218
29 0x7C40 LIC-10213
30 0x7D40 FIC-10216
31 0x7E40 TIC-10211
32 0x7F40 PIC-10211A

3-3. Signal Tag (1-1000번) — TAG

Modbus_addr(N) = 0x2000 + (N - 1) × 2         (N = 1 ~ 1000)

데이터 타입: float 32 (2 레지스터), Read-only

Signal Tag 번호와 HC900 태그명의 매핑은 HC Designer "Tag Information" 리포트 또는
C4-All-Modbus-Map.csv > Signal Tags 1-1000 파티션 참조.

3-4. Variable / Math Variable (1-600번) — MATH_VAR

Modbus_addr(N) = 0x18C0 + (N - 1) × 2         (N = 1 ~ 600)

데이터 타입: float 32 (2 레지스터), Read/Write

4. 주소 영역 요약

Partition Experion 키워드 시작 주소 끝 주소 공식 개수
System Parameters 0x0000 0x003F
PID Loop 1-24 LOOP 0x0040 0x17FF 0x0040+(N-1)×0x100 24 loops
PID Loop 25-32 LOOPX 0x7840 0x7FFF 0x7840+(N-25)×0x100 8 loops
Variable 1-600 MATH_VAR 0x18C0 0x1D6F 0x18C0+(N-1)×2 600 tags
Signal Tag 1-1000 TAG 0x2000 0x27CF 0x2000+(N-1)×2 1000 tags
Signal Tag 1-4000 0x3B60 0x5A9F 0x3B60+(N-1)×2 4000 tags

5. Loop 파라미터 오프셋 테이블

C4-All-Modbus-Map.csv Loop 1 (FIC-9101, base=0x0040) 기준 실측값.
모든 루프에 동일하게 적용된다.

5-1. Float 파라미터 (float 32 = 2 레지스터)

Experion 파라미터명 HC900 태그 접미사 Offset (Hex) Offset (Dec) Access 설명
PV .PV +0x0000 +0 R 프로세스 변수
RSP .RSP_SP2 +0x0002 +2 R/W Remote SP (SP2)
WSP .WSP +0x0004 +4 R/W Working Set Point
OP .Output +0x0006 +6 R/W Output
.PV__B_ +0x0008 +8 R PV (B bank)
.Gain_1_Prop_Band_1 +0x000C +12 R/W Gain 1 / Prop Band 1
.Direction +0x000E +14 R Direction (0=Direct, 1=Reverse)
RESET1 .Reset_1 +0x0010 +16 R/W Reset 1 (Integral)
RATE1 .Rate_1 +0x0012 +18 R/W Rate 1 (Derivative)
.Scan_Cycle_Time +0x0014 +20 R Scan Cycle Time
.PV_low_range +0x0016 +22 R PV Low Range
.PV_high_range +0x0018 +24 R PV High Range
.Alarm_1_SP1 +0x001A +26 R/W Alarm 1 SP1
.Alarm_1_SP2 +0x001C +28 R/W Alarm 1 SP2
.Gain2_Prop_Band_2 +0x0020 +32 R/W Gain 2 / Prop Band 2
.Reset_2 +0x0024 +36 R/W Reset 2
.Rate_2 +0x0026 +38 R/W Rate 2
LSP1 .LSP1 +0x002A +42 R/W Local SP 1
LSP2 .LSP2 +0x002C +44 R/W Local SP 2
.Alarm_2_SP1 +0x002E +46 R/W Alarm 2 SP1
.Alarm_2_SP2 +0x0030 +48 R/W Alarm 2 SP2
.SP_low_limit +0x0034 +52 R/W SP Low Limit
.SP_high_limit +0x0036 +54 R/W SP High Limit
.Output_Low_Limit +0x003A +58 R/W Output Low Limit
.Output_High_Limit +0x003C +60 R/W Output High Limit
.Ratio +0x0046 +70 R/W Ratio
.Bias +0x0048 +72 R/W Bias
.Deviation +0x004A +74 R Deviation (SP-PV)
.Manual_Reset +0x004E +78 R/W Manual Reset
.Feed_forward_Gain +0x0050 +80 R/W Feedforward Gain

5-2. Integer 파라미터 (unsigned 16 = 1 레지스터)

Experion 파라미터명 HC900 태그 접미사 Offset (Hex) Offset (Dec) Access 설명
.Enable_Disable_Fuzzy +0x00B7 +183 R/W Fuzzy Enable (0/1)
.Demand_Tune_Req +0x00B8 +184 R/W Autotune Request
AMSTAT .Auto_Man_State +0x00BA +186 R/W 0=Manual, 1=Auto
.LSP_Select_State +0x00BB +187 R/W SP 선택 (0=SP1, 1=SP2)
.Rem_Loc_SP_State +0x00BC +188 R/W Remote/Local SP
.Tune_Set_State +0x00BD +189 R/W Tune Set
LOOPSTAT .Loop_Status_Register +0x00BE +190 R Loop 상태 (bit-packed)

Loop 블록 크기: 루프당 0x00C0 (192) 레지스터 할당 (0x0040~0x00FF)
float 32 = 2 레지스터 사용 (even address), unsigned 16 = 1 레지스터


6. 주소 계산 예시

예시 1: FICQ-9101 (AnalogPoint) — C4 LOOP 1 LOOPSTAT

LOOP 번호: 1
base_addr = 0x0040 + (1-1) × 0x0100 = 0x0040
LOOPSTAT 오프셋 = +0x00BE
Modbus_addr = 0x0040 + 0x00BE = 0x00FE (= 255 dec)

HC900 태그: FIC-9101.Loop_Status_Register  @ 0x00FE

예시 2: FICQ-9101C4 LOOP 1 AMSTAT (Auto/Manual State)

base_addr = 0x0040
AMSTAT 오프셋 = +0x00BA
Modbus_addr = 0x0040 + 0x00BA = 0x00FA (= 250 dec)

HC900 태그: FIC-9101.Auto_Man_State  @ 0x00FA

예시 3: FICQ-10214 (AnalogPoint) — C4 LOOPX 27 LOOPSTAT

LOOPX 번호: 27  (25번부터 시작)
base_addr = 0x7840 + (27-25) × 0x0100 = 0x7840 + 0x0200 = 0x7A40
LOOPSTAT 오프셋 = +0x00BE
Modbus_addr = 0x7A40 + 0x00BE = 0x7AFE

HC900 태그: FICQ-10214.Loop_Status_Register  @ 0x7AFE

예시 4: P-9114 (StatusPoint) — C4 TAG 32 VALUE

TAG 번호: 32
Modbus_addr = 0x2000 + (32-1) × 2 = 0x2000 + 62 = 0x203E (= 8254 dec)

HC900 태그: P_9114  @ 0x203E  (C4-All-Modbus-Map.csv Signal Tag #32)

예시 5: XV-9101 (StatusPoint) — C4 MATH_VAR 1 VALUE (OP source)

MATH_VAR 번호: 1
Modbus_addr = 0x18C0 + (1-1) × 2 = 0x18C0

HC900 태그: XV9101AUTO  @ 0x18C0  (C4-All-Modbus-Map.csv Variable #1)

7. 검증 결과 요약 (C4 컨트롤러)

Sinam_Tag_all.xlsx × C4-All-Modbus-Map.csv 교차 검증 결과:

패턴 매핑 건수 검증 상태
C4 LOOP N * (1-24) 24 ✓ 전수 일치
C4 LOOPX N * (25-32) 8 ✓ 전수 일치
C4 TAG N VALUE ~120 ✓ 전수 일치
C4 MATH_VAR N VALUE ~55 ✓ 전수 일치
합계 207

8. Python 공식 구현

def experion_indexed_to_modbus(indexed_addr: str, loop_tag_map: dict) -> dict | None:
    """
    Experion Indexed Address → HC900 Modbus 주소 변환.
    
    loop_tag_map: {loop_num: hc900_base_tag}
        예: {1: 'FIC-9101', 2: 'FICQ-9114', ...}
    
    반환: {'hc900_tag': str, 'modbus_addr': int, 'dtype': str}
    """
    import re

    # 파라미터 → (offset, dtype)
    PARAM_OFFSET = {
        'PV':       (0x0000, 'float32'),
        'RSP':      (0x0002, 'float32'),
        'WSP':      (0x0004, 'float32'),
        'OP':       (0x0006, 'float32'),
        'RESET1':   (0x0010, 'float32'),
        'RATE1':    (0x0012, 'float32'),
        'LSP1':     (0x002A, 'float32'),
        'LSP2':     (0x002C, 'float32'),
        'AMSTAT':   (0x00BA, 'uint16'),
        'LOOPSTAT': (0x00BE, 'uint16'),
        'VALUE':    (0x0000, 'float32'),
    }

    # LOOP / LOOPX
    m = re.match(r'\w+ (LOOP|LOOPX) (\d+) (\w+)', indexed_addr)
    if m:
        loop_type, n, param = m.group(1), int(m.group(2)), m.group(3)
        base = (0x0040 + (n-1)*0x0100) if loop_type == 'LOOP' \
               else (0x7840 + (n-25)*0x0100)
        offset, dtype = PARAM_OFFSET.get(param, (None, None))
        if offset is None:
            return None
        hc900_base = loop_tag_map.get(n, f'LOOP{n}')
        suffix = {
            'PV':'PV','RSP':'RSP_SP2','WSP':'WSP','OP':'Output',
            'RESET1':'Reset_1','RATE1':'Rate_1','LSP1':'LSP1','LSP2':'LSP2',
            'AMSTAT':'Auto_Man_State','LOOPSTAT':'Loop_Status_Register',
        }.get(param, param)
        return {'hc900_tag': f'{hc900_base}.{suffix}',
                'modbus_addr': base + offset, 'dtype': dtype}

    # TAG (Signal Tag)
    m = re.match(r'\w+ TAG (\d+) VALUE', indexed_addr)
    if m:
        n = int(m.group(1))
        return {'hc900_tag': f'SIG_TAG_{n}',  # 실제 태그명은 CSV 조회 필요
                'modbus_addr': 0x2000 + (n-1)*2, 'dtype': 'float32'}

    # MATH_VAR (Variable)
    m = re.match(r'\w+ MATH_VAR (\d+) VALUE', indexed_addr)
    if m:
        n = int(m.group(1))
        return {'hc900_tag': f'VAR_{n}',      # 실제 태그명은 CSV 조회 필요
                'modbus_addr': 0x18C0 + (n-1)*2, 'dtype': 'float32'}

    return None


# 사용 예
loop_tag_map = {
    1: 'FIC-9101', 2: 'FICQ-9114', 3: 'FIC-9113',
    # ... (C4-All-Modbus-Map.csv에서 생성)
}

result = experion_indexed_to_modbus('C4 LOOP 1 LOOPSTAT', loop_tag_map)
# → {'hc900_tag': 'FIC-9101.Loop_Status_Register', 'modbus_addr': 0x00FE, 'dtype': 'uint16'}

result = experion_indexed_to_modbus('C4 TAG 32 VALUE', loop_tag_map)
# → {'hc900_tag': 'SIG_TAG_32', 'modbus_addr': 0x203E, 'dtype': 'float32'}

9. 주의사항

  1. 이 공식은 Fixed Map 기준: Custom Map을 사용하는 컨트롤러에서는 주소가 다를 수 있다.
    C4-All-Modbus-Map.csv ("Modbus All Partitions Report")가 실제 매핑의 최종 정보원.

  2. 컨트롤러별 루프 배치가 다름: C1~C4 각 컨트롤러마다 Loop #1에 배치된 PID 블록이 다르다.
    이 문서의 "Loop 번호 → HC900 태그" 테이블은 C4 컨트롤러 전용.

  3. Signal Tag 실제 태그명: TAG N VALUE → Modbus 주소는 공식으로 계산하지만,
    HC900 태그명 (예: P_9114)은 반드시 C4-All-Modbus-Map.csv의 Signal Tags 파티션을 조회해야 한다.

  4. Float 바이트 오더: HC900 기본값은 FP B (Big Endian), 4바이트 순서 = High word first.
    Modbus 레지스터 2개 읽은 뒤 struct.pack('>HH', r[0], r[1])로 디코딩.

  5. MATH_VAR = Variables: Experion 용어 "MATH_VAR"은 HC900 "Variable" (0x18C0~)을 가리킨다.
    R/W 가능 (Write: FC06 단일 또는 FC16 다중 레지스터).