Files
HC900-Crawler/docs/작업지시-PointBuilder-Sinam파싱-UI통합.md
windpacer d88784635e docs: 작업지시·진단·아키텍처 설계 문서 추가
온도프로파일/PV일관성/PointBuilder/history 작업지시, 신호태그·스팀유량 진단, 베이직아키텍처 재설계, MSDS, LLM채팅 구조 등.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 08:12:01 +09:00

9.6 KiB

작업지시: Point Builder 탭에 Sinam xlsx 파싱 UI 통합

2026-06-09. build_register_map_from_sinam.py(다중섹션 파서, .PV 일관화 + 지시계 흡수 포함)를 웹 UI에서 구동하도록 Point Builder 탭에 기능 추가. 다른 세션/LLM이 이 문서로 구현 가능하도록 작성.

진단 결과 (diagnosis-checklist.md 기준)

STEP 결과
STEP 1-2 설계 문서. Point Builder 탭 + Sinam xlsx 파싱 UI 통합. 의존: PointBuilderController.cs, pb.html/js, build_register_map_from_sinam.py, ControllerProcessManager
STEP 3 전체 파일 읽음 (설계문서 110행, Controller 204행, pb.html 294행, pb.js 321행, 스크립트 670행, ProcMgr 294행)
STEP 4 pb.jsPointBuilderController(업로드/파싱) → Process.Start(Python spawn) → SetupController(게이트웨이 재시작)
STEP 5 🟠 MED 4건, 🟡 LOW 2건 (아래 참조)
STEP 6 교차검증 통과 (실제 코드와 대조 완료)
STEP 7-8 아래 보고

[1]. 스크립트 stdout이 사람용 텍스트 — C# 파싱 불안정 (MED)

문제: 문서는 stdout 파싱을 전제하지만, build_register_map_from_sinam.py:645-650의 stdout은 사람용 자유형 텍스트(f'{len(registers)} registers', f'archive=true: {n_archive}', f'흡수(중복 신호점 제거): {len(absorbed_bases)}개 base...'). 정규식 파싱이 가능하나 출력 형식 변경 시 C# 파싱이 깨짐.

근거: build_register_map_from_sinam.py:645-650 — 구조화 출력 없음 영향: UI 파싱 결과 요약(registers/archive/absorbed/loops)이 항상 0 또는 누락으로 표시됨 수정: 스크립트 말미에 machine-readable JSON summary 라인 추가 (또는 C#이 register-map JSON을 직접 읽어 결과 추출)

[2]. 고아 정리 — NOT IN에 수천 개 IN-list는 위험 (MED)

문제: 문서 line 57-61 NOT IN (<register-map JSON 태그 목록>) — 수천 개 IN-list로 SQL 길이 폭발, PostgreSQL NOT IN의 NULL 처리 문제 발생 가능.

근거: 문서 line 57-61 — WHERE m.tagname NOT IN (<JSON 태그 목록>) 영향: 고아 정리 실패 또는 불완전 수행으로 map_master 잔존 태그 발생 수정: 임시 staging 테이블 + NOT EXISTS 패턴 사용 (문서 §3 반영)

[3]. 에러 처리 및 타임아웃 부재 (MED)

문제: 문서 line 33-45에 Process 실패·타임아웃·stderr 캡처 명시 없음. 단발성 프로세스이므로 McpServerHostedService의 헬스체크 패턴과 다름.

근거: 문서 line 33-45 — stdout/결과 반환만 언급, 실패 경로 없음 영향: xlsx 파싱 실패 시 사용자에게 의미 있는 오류 없이 빈 결과 수정: 최대 제한시간 + ExitCode 확인 + stderr 캡처 명시 필요

[4]. DSN 명령줄 인자 — 공백/따옴표 처리 위험 (MED)

문제: --db-conn "host=... password=postgres options=-csearch_path=hc900" — DSN 내 공백 다수. shell=false + 리스트 인자 필수, 문서는 --db-conn "{psycopg2 DSN}"로만 표기되어 구현자가 shell=true 선택 위험.

근거: 문서 line 34-40, 47-52 — DSN 변환 유틸 제시하나 인자 전달 방식 불명확 영향: 쉘 인젝션 또는 DSN 파싱 실패로 DB 연결 불가 수정: 문서에 ArgumentList 사용 강제 명시

[5]. WorkingDirectory 하드코딩 (LOW)

문제: 문서 line 45 — /home/windpacer/projects/hc900_ax 절대경로. McpServerHostedService.cs:19McpServer:WorkingDirectory 설정값을 읽는 것과 대조적.

근거: 문서 line 45 영향: 다른 환경 배포 시 수정 필요 수정: appsettings.json 설정값 사용

[6]. 연속 클릭 방지 구현 부재 (LOW)

문제: 문서 line 98 "버튼 비활성화 + 진행표시" 언급만 있고 pb.js에 구체 로직 없음. 파싱 중복 실행 가능.

근거: 문서 line 98, pb.js pbSinamParse() 미구현 영향: 파싱 중복 실행, DB upsert 충돌 수정: 버튼 비활성화/재활성화 패턴 명시

목표 (사용자 요구)

  1. 웹에서 xlsx 파일 선택(업로드).
  2. [파싱 시작] 버튼 → 파싱 실행.
  3. 파싱된 태그를 목록에서 add/삭제·활성화/비활성화 (기존 Point Builder 목록 재사용).

설계 결정

  • 위치 = Point Builder 탭. 이미 map_master 태그셋 관리(목록·페이징·add·delete·build/apply)를 담당 → 파싱(태그셋 생성)을 같은 탭에 두는 게 일관. xlsx = 단일 진실 공급원(CLAUDE.md).
  • C#이 Python 스크립트를 spawn(재사용), C# 포팅 금지(다중섹션 파싱 중복 방지).
    • 기존 패턴 재사용: src/Infrastructure/Mcp/McpServerHostedService.cs(Process.Start로 uv run 구동), 파일 업로드 /api/kb/upload·/api/pid/upload·/api/docs/upload.

백엔드 (src/Hc900Crawler/Controllers/PointBuilderController.cs 확장)

1) 업로드

POST /api/pointbuilder/sinam/upload   (multipart/form-data)
  • 검증: 확장자 .xlsx, 크기 상한(예: 50MB).
  • 저장 위치: docs/uploads/Sinam_<timestamp>.xlsx (또는 임시 디렉터리). 반환: { file: "<path>" }.

2) 파싱 (dry-run / apply)

POST /api/pointbuilder/sinam/parse
body: { file: string, controller: "C1|C2|C3|C4", applyDb: bool }
  • C#에서 Process 구동:
    python3 scripts/build_register_map_from_sinam.py
        --controller {controller}
        --sinam {file}
        -o docs/register-map-{controller.lower()}.json
        [--db-conn "{psycopg2 DSN}"]   # applyDb=true 일 때만
    
    • applyDb=false(dry-run): --db-conn 생략 → JSON만 생성, DB 미변경. 결과 미리보기용.
    • applyDb=true: --db-conn 포함 → map_master/tag_metadata 갱신(흡수 DELETE 포함).
  • stdout 파싱해서 반환: { registers, archive, absorbed, loops, stdout } (스크립트는 N registers, 흡수(중복 신호점 제거): M개, archive=true: K, N loops expanded 출력).
  • WorkingDirectory = 프로젝트 루트(/home/windpacer/projects/hc900_ax).

DSN 변환 유틸 (필수)

C# 연결문자열(.NET) → psycopg2 DSN. appsettings DefaultConnection (Host=localhost;Port=5432;Database=iiot_platform;...;Search Path=hc900) → host=localhost port=5432 dbname=iiot_platform user=postgres password=postgres options=-csearch_path=hc900.

  • 키 매핑: Host→host, Port→port, Database→dbname, Username→user, Password→password, Search Path=hc900options=-csearch_path=hc900.

3) 고아 정리 (apply 후 자동 호출 또는 parse 내부)

스크립트는 upsert만 하므로, 재생성된 register-map JSON에 없는 해당 컨트롤러 map_master 태그를 비활성/삭제해야 한다(롤아웃 문서와 동일):

DELETE FROM hc900.hc900_map_master m
WHERE m.controller_id = @ctrl
  AND m.tagname NOT IN (<register-map JSON 태그 목록>);

(JSON 태그를 C#에서 읽어 파라미터화, 또는 임시 staging 테이블 사용.)

프론트엔드 (wwwroot/panes/pb.html 상단 카드 + wwwroot/js/pb.js)

pb.html — 요약 카드 위에 "Sinam 파싱" 카드 추가

<div class="pb-right-card">  <!-- 또는 상단 풀폭 카드 -->
  <h3>Sinam xlsx 파싱</h3>
  <input type="file" id="pb-sinam-file" accept=".xlsx">
  <select id="pb-sinam-ctrl"><option>C1</option>...<option>C4</option></select>
  <label><input type="checkbox" id="pb-sinam-apply"> DB 적용(미체크=미리보기)</label>
  <button class="btn-a" onclick="pbSinamParse()">파싱 시작</button>
  <button class="btn-c" onclick="pbSinamGwRestart()">게이트웨이 재시작</button>
  <div id="pb-sinam-result"></div>
</div>

pb.js — 함수 추가

async function pbSinamUpload(file){ /* FormData POST /sinam/upload → {file} */ }
async function pbSinamParse(){
  // 1) 파일 업로드 → path
  // 2) POST /sinam/parse {file, controller, applyDb}
  // 3) 결과(registers/absorbed/loops) 표시
  // 4) applyDb면 pbRefresh()+pbLoadSummary() 로 목록 갱신
}
function pbSinamGwRestart(){ /* 해당 컨트롤러 게이트웨이 재시작 API 호출 */ }
  • 파싱 후 기존 "전체 태그 목록"이 자동 갱신되면 add/삭제·활성/비활성은 기존 기능 그대로 사용.

안전장치 (파싱 = 파괴적 재생성)

  1. dry-run → 확인 → 적용 2단계. applyDb=false로 먼저 미리보기, 사용자 확인 후 적용.
  2. 적용 시 confirm() ("해당 컨트롤러 태그셋을 xlsx 기준으로 재생성합니다").
  3. 고아 정리 자동 수행(위 SQL).
  4. 게이트웨이 재시작: register-map JSON 변경 → 해당 컨트롤러 게이트웨이 재기동해야 새 태그 폴링. (ControllerProcessManager 재시작 엔드포인트 활용/추가.)
  5. 동시성/장시간: 파싱 수 초 소요 → 버튼 비활성화 + 진행표시. 대용량 업로드 타임아웃 주의.

선행 조건 (순서 중요)

지금 스크립트엔 .PV 일관화 + 지시계 흡수가 들어있다(docs/작업지시-PV일관성-롤아웃.md). UI 파싱이 이를 그대로 적용하면 기존 bare명(realtime/history)·디지털 검출과 불일치가 생긴다. → PV 롤아웃(다운스트림 디지털 검출 코드 수정 + 데이터 마이그레이션)을 먼저 완료한 뒤 UI 파싱 표준화를 적용할 것. 그 전엔 UI 파싱을 dry-run 전용으로 두는 것도 방법.

참고

  • 스크립트 CLI: --controller(필수) --sinam(필수) -o(출력) --db-conn(있으면 DB 적용) --validate-csv(선택).
  • 관련 메모리: sinam-xlsx-multisection-parsing, tag-naming-and-map-master.
  • 관련 문서: 작업지시-PV일관성-롤아웃.md, 작업지시-history-디지털잔재정리.md.