베이직 아키텍처 · 태그 디자인 전면 재설계
본 문서는 HC900-AX 프로젝트의 전면 재설계 결과를 정의한다.
2026-06-08, 전면 개정.
목차
- 핵심 원칙
- HC900 Modbus 통신 규칙
- Sinam_Tag_all.xlsx 데이터 구조
- 주소 변환 규칙 (공식 검증 완료)
- 태그명 체계 및 등록 규칙
- register-map-cN.json 포맷
- 아카이브/모니터링 제어
- 컨트롤러별 독립 맵 전략
- 데이터 흐름 (전면 개정)
- build_register_map_from_sinam.py 처리 규칙
- C# 서비스 수정 사항
- tag_metadata 적재
- 알려진 문제
- 폐기 항목
- 마이그레이션 순서
1. 핵심 원칙
1.1 단일 진실 공급원 (Single Source of Truth)
docs/Sinam_Tag_all.xlsx 가 모든 태그 정보의 유일한 출처
- HC Designer CSV (
SignalTags.csv, Variables.csv, SummaryFunctionBlockReport.csv)는 주소 검증용 보조 자료
- Experion OPC UA legacy (소문자 태그명, node_id 등)는 완전히 폐기
1.2 태그명 표기법
- 모든 태그명은 대문자 (OPC UA 강제 소문자 규칙 폐기)
- 구분자:
- (하이픈) + . (파라미터)
- 예:
FICQ-6101.PV, P-9114.STATE, LI-9100.PV
- Experion ItemName 기준 (
FICQ-6101), HC900 네이티브명(FIQ6101) 사용 안 함
1.3 컨트롤러별 독립 맵
- 각 HC900 컨트롤러(C1~C4)는 독립적인
register-map-cN.json 사용
config/gateway-config.json에서 각 컨트롤러의 enabled/disabled 관리
- 웹 UI Setup 페이지에서 컨트롤러 추가/삭제/시작/중지 가능
1.4 레지스터 맵 엔트리 설계 원칙
- ❌ Loop 블록 통째로(192 regs) 등록하지 않음 — 게이트웨이의 배치 그룹핑이 개별 엔트리를 쪼갤 수 없으므로
count > 120 인 단일 엔트리는 단일 FC03 호출로 192 regs 읽기를 시도하여 HC900에서 실패
- ✅ 각 파라미터를 개별 엔트리로 등록 (PV=2regs, SP=2regs, OP=2regs, MODE=1reg, ...)
- 게이트웨이가 주소 순서로 정렬 후 인접 엔트리들을 동적으로 ≤120 배치로 묶어서 FC03 호출
2. HC900 Modbus 통신 규칙
2.1 기본 프로토콜
| 항목 |
값 |
근거 |
| 전송 |
Modbus TCP |
modbus_tcp.cpp |
| HC900 IP |
컨트롤러별 설정 |
gateway-config.json |
| 포트 |
502 |
기본값 |
| Unit ID |
1 |
modbus_tcp.cpp:13 |
| 소켓 timeout |
2초 |
modbus_tcp.cpp:14 |
| Watchdog |
5초 무응답 → Reconnecting |
modbus_tcp.cpp:15 |
| Max retry |
5회 → Fault (30초 후 자동 재시도) |
modbus_tcp.cpp:16 |
| Float format |
FP B (IEEE 754 BigEndian, HighFirst, Normal) |
HC900 매뉴얼, vendor_formats.hpp |
2.2 Function Codes
| 기능 |
FC |
방향 |
구현 |
| Read Holding Registers |
FC03 |
HC900 → Gateway |
modbus_tcp.cpp:171 |
| Write Multiple Registers |
FC16 (0x10) |
Gateway → HC900 |
modbus_tcp.cpp:251 |
2.3 배치 읽기 규칙 (C++ 게이트웨이 자동 수행)
게이트웨이의 ReadAllRegisters()는 런타임에 register-map 엔트리들을 주소 순서로 정렬한 후, 동일한 120-register 윈도우 안에 들어가는 엔트리들을 한 번의 FC03로 묶어서 읽음:
중요: 개별 엔트리의 addr + count가 120을 넘으면 단일 FC03로 처리되므로, 192-regs 통째 등록은 불가능. 각 파라미터는 count=1(uint16) 또는 2(float32)로 등록해야 함.
2.4 Loop 블록 구조 (매뉴얼 Table 6-3)
각 PID 루프는 256개 레지스터 블록 (루프 블록 크기 = 192 regs, 0x40~0xFF).
| Offset |
파라미터 |
타입 |
본문 접근 |
| 0x00 |
PV |
float32 |
R |
| 0x02 |
Remote SP (SP2) |
float32 |
R |
| 0x04 |
Working Set Point (SP) |
float32 |
RW |
| 0x06 |
Output (OP) |
float32 |
RW |
| 0x0C |
Gain #1 |
float32 |
RW |
| 0x0E |
Direction |
float32 |
R |
| 0x10 |
Reset #1 |
float32 |
RW |
| 0x12 |
Rate #1 |
float32 |
RW |
| 0x14 |
Cycle Time #1 |
float32 |
R |
| 0x16 |
PV Low Range |
float32 |
R |
| 0x18 |
PV High Range |
float32 |
R |
| 0x1A |
Alarm #1 SP #1 (AL1SP1) |
float32 |
RW |
| 0x1C |
Alarm #1 SP #2 (AL1SP2) |
float32 |
RW |
| 0x20 |
Gain #2 |
float32 |
RW |
| 0x2A |
Local SP #1 (LSP1) |
float32 |
RW |
| 0x2C |
Local SP #2 (LSP2) |
float32 |
RW |
| 0x2E |
Alarm #2 SP #1 (AL2SP1) |
float32 |
RW |
| 0x30 |
Alarm #2 SP #2 (AL2SP2) |
float32 |
RW |
| 0x34 |
SP Low Limit |
float32 |
RW |
| 0x36 |
SP High Limit |
float32 |
RW |
| 0x3A |
Output Low Limit |
float32 |
RW |
| 0x3C |
Output High Limit |
float32 |
RW |
| 0x3E |
Working Output (OPWORK) |
float32 |
RW |
| 0x46 |
Ratio |
float32 |
RW |
| 0x48 |
Bias |
float32 |
RW |
| 0x4A |
Deviation |
float32 |
R |
| 0x4E |
Manual Reset |
float32 |
RW |
| 0x50 |
Feedforward Gain |
float32 |
RW |
| 0x52 |
Local %CO |
float32 |
RW |
| 0xB7 |
Fuzzy Enable |
uint16 |
RW |
| 0xB8 |
Autotune Request |
uint16 |
RW |
| 0xB9 |
Anti-soot Enable |
uint16 |
RW |
| 0xBA |
Auto/Manual State (MODE write target) |
uint16 |
RW |
| 0xBB |
SP Select State |
uint16 |
RW |
| 0xBC |
Remote/Local SP State |
uint16 |
RW |
| 0xBE |
Loop Status (MODE read source) |
uint16 |
R |
2.5 루프 주소 계산
검증 완료 (SummaryFunctionBlockReport.csv):
| Loop |
CSV 주소 |
계산 |
일치 |
| #1 |
0x0040 |
0x0040 + 0*0x0100 |
✅ |
| #11 |
0x0A40 |
0x0040 + 10*0x0100 |
✅ |
| #18 |
0x1140 |
0x0040 + 17*0x0100 |
✅ |
| #24 |
0x1740 |
0x0040 + 23*0x0100 |
✅ |
| #25 |
0x7840 |
0x7840 + 0*0x0100 |
✅ |
| #32 |
0x7F40 |
0x7840 + 7*0x0100 |
✅ |
2.6 주소 영역
| 영역 |
시작 |
끝 |
설명 |
| Misc |
0x0000 |
0x003F |
기타 파라미터 |
| Loop 1~24 |
0x0040 |
0x15FF |
루프 블록 (각 256 regs) |
| Loop 25~32 |
0x7840 |
0x78FF |
루프 블록 확장 |
| Analog Input |
0x1800 |
0x187F |
AI 값 (Function Code 03) |
| Variable |
0x18C0 |
0x1D6F |
R/W 변수 (MATH_VAR) |
| Signal Tag |
0x2000 |
0x27CF |
R 태그 (TAG) |
3. Sinam_Tag_all.xlsx 데이터 구조
3.1 파일 개요
- 1개 시트 (Sheet1)
- 3407 데이터 행, 150 컬럼
- 다중 섹션 구조
3.2 Section 구성
| Section |
행 범위 |
헤더 첫 컬럼 |
설명 |
| 1 (ItemName) |
Row 2~ |
ItemName |
메인 태그 정의 (AnalogPoint, StatusPoint, HistoryParameters) |
| 2+ (ParentItemName) |
Row 1163~4424 |
ParentItemName |
FlexibleParameters 확장 속성 |
3.3 Section 1: ItemName — 주요 컬럼
| 컬럼명 |
0-indexed |
예시 |
설명 |
ItemName |
0 |
FICQ-6101 |
Experion 태그명 (대문자) |
Class |
1 |
AnalogPoint / StatusPoint / HistoryParameters |
태그 클래스 |
ItemDescription |
5 |
FICQ-6101 PV |
설명 |
DownloadedName |
6 |
FICQ-6101 |
다운로드명 |
AreaCode |
11 |
P6 |
영역 코드 |
TrendParameter |
18 |
True / PV / False |
Experion 트렌드 대상 (참고용, 우리 시스템과 무관) |
SourceAddressPV |
29 |
C3 LOOP 11 PV / C4 TAG 32 VALUE |
PV의 HC900 주소 |
SourceAddressOP |
30 |
C3 LOOP 11 OPWORK / C4 MATH_VAR 5 VALUE |
OP의 HC900 주소 |
SourceAddressMD |
31 |
C3 LOOP 11 LOOPSTAT |
MODE의 HC900 주소 |
DescriptorState0~7 |
61~68 |
RUN, STOP, FAULT |
상태 레이블 (StatusPoint 한정) |
Units |
(AnalogPoint) |
kg/h |
엔지니어링 단위 |
RangeLow / RangeHigh |
(AnalogPoint) |
0 / 100 |
측정 범위 |
3.4 Section 2+: ParentItemName (FlexibleParameters)
| 컬럼명 |
0-indexed |
예시 |
설명 |
ParentItemName |
0 |
FICQ-6101 |
부모 태그명 |
Class |
1 |
FlexibleParameters |
고정 |
ParamName |
2 |
QV, RST, STATE, AL1SP1, HZ |
확장 파라미터명 |
TypeDatabaseReference |
5 |
16 bit signed integer (INT2) |
데이터타입 |
ScanPeriodPVUDSP |
25 |
2 |
스캔 주기 (초) |
UnitsUDSP |
26 |
kg |
단위 |
RangeHighUDSP / RangeLowUDSP |
27/28 |
범위 |
|
StatusNumStatesUDSP |
32 |
2 |
상태 개수 |
DescriptorState0~7UDSP |
33~40 |
OFF, ON |
상태 레이블 (Status 한정) |
DestinationAddressPVUDSP |
23 |
C3 MATH_VAR 37 VALUE |
쓰기 주소 (있으면 RW, 없으면 R) |
3.5 SourceAddress 형식
| 형식 |
예시 |
변환 규칙 |
C{N} TAG {N} VALUE |
C4 TAG 32 VALUE |
addr = 0x2000 + (N-1)*2, access=R |
C{N} MATH_VAR {N} VALUE |
C4 MATH_VAR 5 VALUE |
addr = 0x18C0 + (N-1)*2, access=RW |
C{N} LOOP {N} {PARAM} |
C3 LOOP 11 PV |
루프 블록 내 파라미터 오프셋 |
C{N} LOOPX {N} {PARAM} |
C3 LOOPX 25 WSP |
LOOPX = Loop 25~32와 동일 주소 체계 |
4. 주소 변환 규칙 (공식 검증 완료)
Sinam_Tag_all.xlsx의 SourceAddress 문자열을 HC900 Modbus 주소로 변환하는 공식:
4.1 TAG N → Modbus 주소
검증: CSV TAG 4 = FIQ6101 @ 0x2006 ✅
4.2 MATH_VAR N → Modbus 주소
검증: CSV VAR 2 = LT3211_LSET @ 0x18C2 ✅
4.3 LOOP N PARAM → Modbus 주소
검증: C2 LOOP 18 WSP → base=0x1140, off=0x04 → addr=0x1144 ✅
4.4 Mnemonic → Offset 매핑
| Mnemonic |
Offset |
Experion Attr |
| PV |
0x00 |
PV |
| RSP / SP2 |
0x02 |
— |
| WSP / SPWORK |
0x04 |
SP |
| OP / OPWORK |
0x06 |
OP |
| GAIN1 / PROP1 |
0x0C |
GAIN |
| DIR |
0x0E |
— |
| RESET1 |
0x10 |
RESET |
| RATE1 |
0x12 |
RATE |
| PVLOW |
0x16 |
— |
| PVHIGH |
0x18 |
— |
| AL1SP1 |
0x1A |
— |
| AL1SP2 |
0x1C |
— |
| LSP1 |
0x2A |
— |
| LSP2 |
0x2C |
— |
| AL2SP1 |
0x2E |
— |
| SPLOW |
0x34 |
SP_LO |
| SPHIGH |
0x36 |
SP_HI |
| OPLOW |
0x3A |
OP_LO |
| OPHIGH |
0x3C |
OP_HI |
| OPWORK |
0x3E |
OP |
| DEV |
0x4A |
— |
| LOOPSTAT |
0xBE |
MD (읽기) |
| MODEIN |
0xBA |
MD (쓰기) |
| AMSTAT |
0xBA |
— |
⚠️ MODE 주의: 읽기는 LOOPSTAT(0xBE), 쓰기는 Auto/Man State(0xBA). build_register_map_from_sinam.py 초기 버전에서 write_addr이 0xBE로 잘못 설정된 버그 있음 → 수정 완료.
5. 태그명 체계 및 등록 규칙
5.1 태그명 포맷
| 유형 |
태그명 예시 |
출처 |
| Loop PV |
FICQ-6101.PV |
ItemName + Mnemonic |
| Loop SP |
FICQ-6101.SP |
WSP/SPWORK → SP |
| Loop OP |
FICQ-6101.OP |
OP/OPWORK → OP |
| Loop MD |
FICQ-6101.MD |
LOOPSTAT(읽기)/MODEIN(쓰기) → MD |
| Loop GAIN |
FICQ-6101.GAIN |
GAIN1 → GAIN |
| Loop RESET |
FICQ-6101.RESET |
RESET1 → RESET |
| Loop RATE |
FICQ-6101.RATE |
RATE1 → RATE |
| Loop 기타 |
FICQ-6101.Deviation |
HC900명 유지 |
| AnalogPoint (TAG source) |
LI-9100 |
Class=AnalogPoint, PV source=TAG |
| StatusPoint PV (TAG) |
P-9114 |
Class=StatusPoint, PV source=TAG |
| StatusPoint OP (MATH_VAR) |
P-9114.OP |
Class=StatusPoint, OP source=MATH_VAR |
| FlexibleParameter |
FICQ-6101.QV / P-9114.STATE |
ParentItemName.ParamName |
5.2 Loop 파라미터 Experion명 매핑
Loop 블록 내 모든 파라미터 중 Experion이 노출하는 것만 Experion 속성명 사용, 나머지는 HC900 네이티브명 유지:
5.3 FlexibleParameter 등록
Section 2+에서 ParentItemName.ParamName 으로 등록:
| FlexibleParameter |
등록명 |
비고 |
| FICQ-6101 / QV |
FICQ-6101.QV |
Quality Value (적산) |
| FICQ-6101 / RST |
FICQ-6101.RST |
Reset |
| FICQ-6101 / AL1SP1 |
FICQ-6101.AL1SP1 |
Alarm Setpoint |
| P-9114 / STATE |
P-9114.STATE |
운전 상태 |
| P-9114 / HZ |
P-9114.HZ |
주파수 |
| P-9114 / HZSET |
P-9114.HZSET |
주파수 설정 |
6. register-map-cN.json 포맷
6.1 엔트리 구조
| 필드 |
타입 |
설명 |
tag |
string |
태그명 (Experion ItemName + 파라미터) |
addr |
uint16 |
Modbus holding register 주소 (0-based) |
write_addr |
uint16 |
쓰기 주소 (기본값 = addr, MODE 등 별도 쓰기 레지스터가 있는 경우 다름) |
count |
uint16 |
레지스터 수 (float32=2, uint16=1) |
type |
string |
데이터 타입 (float32 또는 uint16) |
access |
string |
접근 권한 (R 또는 RW) |
description |
string |
설명 |
archive |
bool |
history_table 기록 대상 여부 |
6.2 Loop 엔트리 (개별 파라미터)
6.3 일반 TAG 엔트리
6.4 Variable 엔트리 (MATH_VAR)
6.5 FlexibleParameter 엔트리
7. 아카이브/모니터링 제어
7.1 문제 정의
현재 시스템은 다음과 같이 동작:
문제점:
realtime_table: GAIN1, RESET1, AL1SP1 등 불필요한 태그까지 표시
history_table: 아카이브할 가치 없는 태그까지 60초마다 기록 (GAIN1, BIAS, RATIO, DEV 등)
is_active 하나로만 제어 → 실시간 표시와 이력 기록을 분리 불가
7.2 대상 선별 규칙
| 태그 유형 |
예시 |
realtime |
history |
근거 |
| Loop PV |
FICQ-6101.PV |
✅ |
✅ |
공정값, 추세 필수 |
| Loop SP |
FICQ-6101.SP |
✅ |
✅ |
설정값 변경 이력 |
| Loop OP |
FICQ-6101.OP |
✅ |
✅ |
출력값 추세 |
| Loop MODE |
FICQ-6101.MD |
✅ |
✅ |
Auto/Man 전환 이력 |
| Loop QV |
FICQ-6101.QV |
✅ |
✅ |
Quality Value 추세 |
| AnalogPoint PV |
LI-9100 |
✅ |
✅ |
일반 공정값 |
| StatusPoint PV |
P-9114 |
✅ |
✅ |
상태 변화 (event_history_table) |
| Loop 기타 파라미터 |
FICQ-6101.GAIN1, .RESET1, .RATE1, .AL1SP1, .DIR, .PV_LO, .PV_HI, .SP_LO, .SP_HI, .DEV |
✅ |
❌ |
설정값, 실시간 확인만 |
| Variable (MATH_VAR) |
P-9114.RST, FICQ-6101.RST |
❌ |
❌ |
중간 계산값, 불필요 |
| StatusPoint OP |
P-9114.OP |
❌ |
❌ |
MATH_VAR 기반 |
| 기타 FlexibleParameter |
P-9114.STATE, FICQ-6101.AL1SP1 |
△ |
❌ |
필요시 사용자 활성화 |
7.3 archive 플래그 선별 로직
7.4 hc900_map_master 컬럼 추가
| 컬럼 |
설명 |
기본값 |
realtime_enabled |
realtime_table 표시 여부 |
TRUE (모든 활성 태그) |
archive_enabled |
history_table 기록 여부 |
register-map의 archive 필드 |
사용자는 PointBuilder UI에서 두 플래그를 개별적으로 override 가능:
| 태그명 |
실시간 |
이력 |
| FICQ-6101.PV |
☑ |
☑ |
| FICQ-6101.SP |
☑ |
☑ |
| FICQ-6101.OP |
☑ |
☑ |
| FICQ-6101.MD |
☑ |
☑ |
| FICQ-6101.QV |
☑ |
☑ |
| FICQ-6101.GAIN1 |
☑ |
☐ |
| LI-9100 |
☑ |
☑ |
| P-9114 |
☑ |
☑ |
| P-9114.RST |
☐ |
☐ |
7.5 마이그레이션
8. 컨트롤러별 독립 맵 전략
8.1 컨트롤러 구성
| 컨트롤러 |
IP |
포트 |
gRPC Port |
현재 상태 |
| C1 |
192.168.0.250 |
502 |
50051 |
미연결 |
| C2 |
192.168.0.230 |
502 |
50052 |
미연결 |
| C3 |
192.168.0.240 |
502 |
50053 |
활성 |
| C4 |
192.168.0.220 |
502 |
50054 |
미연결 |
8.2 설정 파일
8.3 컨트롤러 등급별 구분
각 컨트롤러는 독립된 C++ 게이트웨이 프로세스로 동작:
- 각자의 register-map-cN.json 로드
- 각자의 IP/Port로 Modbus TCP 연결
- 각자의 gRPC 포트에서 서비스
- C# ControllerGrpcClientPool이 enabled 컨트롤러별로 클라이언트 생성
9. 데이터 흐름 (전면 개정)
9.1 전체 흐름
9.2 C++ 게이트웨이 상세
10. build_register_map_from_sinam.py 처리 규칙
10.1 실행 명령어
10.2 처리 순서
- Sinam_Tag_all.xlsx 로드 (openpyxl, read_only=True, data_only=True)
- Section 1 처리 (ItemName 행):
- 각 행의 모든 셀에서 정규식으로 C{N} LOOP/TAG/MATH_VAR/LOOPX/RAW 검색
scan_point() 분류: loop / tag / var / raw
- Loop:
build_loop_entries() → 모든 LOOP_LAYOUT 파라미터를 개별 엔트리로 전개
- Tag:
build_point_entry() → 단일 엔트리
- Var:
build_point_entry() → 단일 엔트리
- Section 2+ 처리 (FlexibleParameters 행):
resolve_one() 으로 개별 주소 해석
ParentItemName.ParamName 태그명 생성
archive 플래그 설정 (should_archive())
- 컨트롤러별 필터링 (--controller 인자)
- 주소 순서 정렬 후 JSON 출력
- 선택적 DB 적재:
tag_metadata upsert (상태 레이블, 단위, desc, area)
hc900_map_master upsert (is_active, realtime_enabled, archive_enabled)
10.3 Loop 엔트리 전개 상세
build_loop_entries(item_name, loop_no, mnemonics):
10.4 MODE write_addr 처리
⚠️ 초기 버전에서 write_addr = base + 0xBE로 잘못 설정된 버그 수정 완료.
올바른 값: 읽기=0xBE(LoopStatus), 쓰기=0xBA(Auto/Man State).
10.5 archive 플래그 설정
11. C# 서비스 수정 사항
11.1 Hc900MapEntry 엔티티
11.2 Hc900RealtimeService.LoadMappingAsync
11.3 Hc900HistoryService.SnapshotToHistoryAsync
11.4 DDL (멱등)
12. tag_metadata 적재
build_register_map_from_sinam.py 실행 시 --db-conn 옵션으로 tag_metadata도 함께 upsert:
| attribute |
출처 (Sinam_Tag_all.xlsx) |
desc |
ItemDescription 또는 DownloadedName |
area |
AreaCode (예: P6, P9) |
state0~7 |
DescriptorState0~7 (StatusPoint) / DescriptorState0~7UDSP (FlexibleParameters) |
units |
Units (AnalogPoint) / UnitsUDSP (FlexibleParameters) |
eulo / euhi |
RangeLow / RangeHigh (AnalogPoint) / RangeLowUDSP / RangeHighUDSP (FlexibleParameters) |
13. 알려진 문제
13.1 MODE write_addr 버그 (수정 완료)
build_register_map_from_sinam.py 초기 버전: MODE 엔트리의 write_addr = base + 0xBE (LoopStatus, 읽기전용)
- 올바름:
write_addr = base + 0xBA (Auto/Man State, R/W)
- 영향: 게이트웨이를 통한 MODE 쓰기(MAN→AUTO 전환)가 HC900에 반영되지 않음
- 해결: 개정 코드에서
MD_WRITE_OFFSET = 0xBA 사용
13.2 LOOPX (Custom Map) 주소 검증
C4 LOOPX 25 AL1SP1 등의 Custom Map 영역
- HC Designer Custom Map 보고서 필요 (현재 SummaryFunctionBlockReport.csv는 Fixed Map 기준)
- 주소 체계 검증: LOOPX 25
32는 Loop 2532와 동일한 0x7840 + (N-25)*0x0100 사용한다고 가정
13.3 C4 PID Loop 데이터 부재
FICQ-9101, FICQ-9214 등 C4 루프 태그는 Sinam_Tag_all.xlsx에 SourceAddress가 있음
- HC Designer CSV는 C4(Custom Map) PID Loop 데이터 없음 (빈 맵)
- PointBuilder로 수동 등록 또는 HC Designer Custom Map Export 필요
13.4 Config 게이트웨이 경로
gateway-config.json이 구버전 register-map-c3.json(2189개 엔트리, HC900명) 참조 중
- 신규
register-map-c3.json(Experion명) 생성 후 경로 업데이트 필요
13.5 hc900_map_master.tagname = hc900_tag 중복
- 현재
tagname(소문자 OPC UA 명)과 hc900_tag(대문자 Experion 명)가 동일해짐
- 향후
tagname 컬럼 제거하고 hc900_tag로 통일 가능
- 마이그레이션 동안은 두 컬럼 모두 동일한 값(Experion 대문자명) 유지
14. 폐기 항목
| 항목 |
사유 |
대체 |
load_state_labels.py |
Sinam_Tag_all.xlsx 통합 |
build_register_map_from_sinam.py에 통합 |
HC Designer CSVs (SignalTags.csv 등) |
주소 검증용 보조 자료 |
Sinam_Tag_all.xlsx가 주 소스 |
OPC UA node_id |
HC900 직결 방식에서 불필요 |
빈 문자열로 저장 |
소문자 태그명 (ficq-6101.pv) |
OPC UA 강제 규칙 폐기 |
대문자 (FICQ-6101.PV) |
build_register_map.py (기존) |
CSV 의존, Experion명 미지원 |
build_register_map_from_sinam.py |
build_register_map_from_csv.py |
CSV Full Map 단순 변환 |
Sinam 기반 스크립트로 대체 |
| 개별 루프 파라미터 → 루프 블록 통째 |
게이트웨이 MAX_BATCH=120 제약 |
개별 파라미터 유지 (게이트웨이가 동적 배치) |
15. 마이그레이션 순서
Phase A: 스크립트 완성
| 단계 |
작업 |
담당 |
| A1 |
build_register_map_from_sinam.py MODE write_addr 버그 수정 (0xBE→0xBA) |
Python |
| A2 |
build_register_map_from_sinam.py에 archive 필드 추가 + 선별 로직 구현 |
Python |
| A3 |
build_register_map_from_sinam.py에 tag_metadata upsert 로직 추가 |
Python |
| A4 |
build_register_map_from_sinam.py에 hc900_map_master upsert 로직 추가 |
Python |
| A5 |
C1~C4 전 컨트롤러 register-map 생성 테스트 |
Python |
Phase B: DB 마이그레이션
| 단계 |
작업 |
담당 |
| B1 |
hc900_map_master에 realtime_enabled, archive_enabled DDL 추가 |
C# |
| B2 |
Phase A에서 생성한 register-map으로 hc900_map_master 초기 적재 |
Python |
Phase C: C# 서비스 수정
| 단계 |
작업 |
담당 |
| C1 |
Hc900MapEntry에 RealtimeEnabled, ArchiveEnabled 속성 추가 |
C# |
| C2 |
Hc900RealtimeService.LoadMappingAsync에 AND realtime_enabled = TRUE 추가 |
C# |
| C3 |
SnapshotToHistoryAsync에 archive_enabled 필터 적용 |
C# |
| C4 |
PointBuilder UI에 실시간/이력 토글 컬럼 추가 |
C# |
Phase D: 배포
| 단계 |
작업 |
담당 |
| D1 |
C++ 게이트웨이 신규 register-map-c3.json으로 교체 |
Config |
| D2 |
gateway-config.json 경로 업데이트 |
Config |
| D3 |
C# 서비스 재시작 |
Ops |
| D4 |
기존 archive_enabled = FALSE → register-map 기준 UPDATE |
SQL |
관련 파일
| 파일 |
설명 |
docs/Sinam_Tag_all.xlsx |
단일 진실 공급원 — 모든 태그 정의 + 메타데이터 |
docs/51-52-25-111-HC900-Process-Controller-Communications-manual.pdf |
HC900 통신 매뉴얼 (Table 6-1, 6-3) |
docs/register-map-cN.json |
컨트롤러별 레지스터 맵 (build_register_map_from_sinam.py 생성) |
config/gateway-config.json |
게이트웨이 프로세스 설정 (IP, port, 맵 경로) |
scripts/build_register_map_from_sinam.py |
단일 빌드 스크립트 — 맵 생성 + 메타데이터 + archive 플래그 |
src/Infrastructure/Hc900/Hc900RealtimeService.cs |
실시간 폴링 서비스 (gRPC → realtime_table) |
src/Infrastructure/Hc900/Hc900HistoryService.cs |
이력 스냅샷 서비스 (realtime_table → history_table) |
src/Infrastructure/Database/Hc900DbContext.cs |
DB 컨텍스트 (테이블 정의, 마이그레이션 + SnapshotToHistoryAsync) |
src/Core/Domain/Entities/Hc900Entities.cs |
Hc900MapEntry 엔티티 (hc900_map_master 매핑) |
industrial-comm/cpp/src/gateway.cpp |
C++ 게이트웨이 (Modbus 폴링 + gRPC + 동적 배치 그룹핑) |
industrial-comm/cpp/src/modbus_tcp.cpp |
Modbus TCP 전송 계층 (FC03, FC16) |
industrial-comm/cpp/src/vendor_formats.hpp |
FP_B float format (bigEndian, highFirst, normal) |