# 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.xlsx`의 `SourceAddressPV` / `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-9101` — `C4 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 공식 구현 ```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 다중 레지스터).