[DICOM] DICOM Association — PACS 통신 디버깅이 일어나는 곳

Oleg Pianykh, Digital Imaging and Communications in Medicine (DICOM) (Springer, 2008) 정리 6편
참고 챕터: Ch 9

 

이전 편에서 우리는 얼버무리고 넘어온 것이 있다.

"두 AE가 통신하려면 먼저 Association 을 맺어야 한다"

 

이번 6편은 그 Association의 정체를 다룬다.

 

PACS 연동에서 가장 자주 마주치는 에러들

  • Association rejected (no reason given)
  • Abstract syntax not supported
  • Transfer syntaxes not supported
  • Implementation version name too long
  • Calling AE not in whitelist

이 모든 게 Association 협상 단계에서 일어난다.

 

저자가 말한 그대로

"DICOM association failures account for the vast majority of all DICOM networking problems."

 

이 글이 끝나면 Orthanc/pynetdicom 로그를 보고 직접 해석할 수 있다.


1. Association이란?

1.1 DICOM Upper Layer (DICOM UL)

DICOM Association은 TCP/IP 위에서 도는 DICOM 전용 통신 계층.

┌─────────────────────────────────────┐
│    DIMSE (Service messages)          │  ← 4-5편
├─────────────────────────────────────┤
│    DICOM Upper Layer (UL)            │  ← 6편 (이번 글)
│    = Association 협상 + PDU 전송      │
├─────────────────────────────────────┤
│    TCP / IP                          │
└─────────────────────────────────────┘

TCP는 그냥 "바이트 스트림 옮기는 통로"일 뿐.

 

DICOM UL이 그 위에

  • "우리가 통신할 수 있는 사이인지" 협상하고
  • "어떤 형식으로 데이터를 주고받을지" 합의하고
  • "실제 데이터를 PDU 단위로 흘려보낸다.

 

1.2 Association = "DICOM 핸드셰이크"

전화 비유:

  • TCP 연결 = 전화선이 깔림
  • Association 협상 = "여보세요, ○○입니다. △△ 하려고요" + "네, 가능합니다"
  • DIMSE 메시지 = 실제 대화
  • Association 종료 = "끊을게요" + "네 끊으세요"

Association 협상 실패하면 그 뒤 통신은 절대 안 됨. TCP는 살아있어도 의미 없음.

 


2. Association의 3막 (직관적 이해) ⭐

저자가 책에서 만든 비유. 이거 하나만 알고 있어도 50% 끝.

Act 1 — Association Establishment (협상)

MR scanner: "Hi, 나 MR scanner고 DICOM 할 줄 알아. 너도?"
ARCHIVE:    "응 나도 DICOM 함"

MR:         "너 MR Image Storage SCP 야?"
ARCHIVE:    "맞아"

MR:         "MR 영상 100장 있어. 압축 안 한 채로 보낼 수도 있고,
             JPEG2000으로 압축해서 보낼 수도 있고, JPEG-LS도 가능."
ARCHIVE:    "OK, MR 받을게. 압축 안 한 거로 받을게."

MR:         "좋아, 보낼게."
ARCHIVE:    "받을 준비 완료."

→ 이게 A-Associate-RQA-Associate-AC 의 본질.

 

Act 2 — Data Transfer (전송)

MR:      "1번 영상." "2번." "3번..."
ARCHIVE: "1번 OK." "2번 OK..."

P-Data-TF PDU로 실제 영상 흐름.

 

Act 3 — Association Termination (종료)

MR:      "100장 다 보냈음. 0장 실패. 성공. 끝낼게."
ARCHIVE: "다 받았음. 끝!"

(연결 종료)

A-Release-RQA-Release-RP.

 


3. Abstract Syntax — "어떤 서비스?"

협상 단계에서 가장 먼저 합의하는 것: 무슨 SOP에 대해 통신할 건지.

 

3.1 Abstract Syntax = SOP Class UID

"Abstract Syntax UID = SOP Class UID" 는 사실상 같은 말. 둘 다 같은 UID를 가리킨다.

Abstract Syntax 1.2.840.10008.5.1.4.1.1.4 = MR Image Storage SOP
                                              ↑
                                        이 SOP를 위해 협상하자

 

3.2 주요 Abstract Syntax UID

분류 Abstract Syntax 이름 UID
Verification Verification 1.2.840.10008.1.1
Storage CR Image 1.2.840.10008.5.1.4.1.1.1
Storage CT Image 1.2.840.10008.5.1.4.1.1.2
Storage MR Image 1.2.840.10008.5.1.4.1.1.4
Storage US Image 1.2.840.10008.5.1.4.1.1.6.1
Storage Secondary Capture 1.2.840.10008.5.1.4.1.1.7
Storage Enhanced SR 1.2.840.10008.5.1.4.1.1.88.22
Q/R Find Study Root 1.2.840.10008.5.1.4.1.2.2.1
Q/R Get Study Root 1.2.840.10008.5.1.4.1.2.2.3
Q/R Move Study Root 1.2.840.10008.5.1.4.1.2.2.2
Q/R Find Patient Root 1.2.840.10008.5.1.4.1.2.1.1
MWL MWL Find 1.2.840.10008.5.1.4.31

 

3.3 인코딩

A-Associate-RQ 메시지 안에서 Abstract Syntax는 이런 형식:

┌──────┬──────────┬─────────┬─────────────────────┐
│ 0x30 │ reserved │ Length  │  UID string         │
│ 1byte│ 1 byte=0 │ 2 bytes │  L bytes            │
└──────┴──────────┴─────────┴─────────────────────┘

 

예: 1.2.840.10008.5.1.4.1.1.4 (MR Image Storage, 25자 = 0x19)

 

30 00 00 19 31 2E 32 2E 38 34 30 2E 31 30 30 30 38 2E 35 2E 31 2E 34 2E 31 2E 31 2E 34

 

3.4 거부 시나리오

ARCHIVE가 MR Image Storage만 지원하고 CT Image Storage는 미지원이라면

CT 장비 → ARCHIVE: "1.2.840.10008.5.1.4.1.1.2 (CT Storage) SCP 야?"
ARCHIVE → CT 장비: "❌ 아니야. Abstract Syntax not supported."
                    → 또는 그 Presentation Context만 거부

 

💡 Abstract Syntax는 협상 불가. 이건 장비 기능 자체. 합의가 아닌 매칭.

 


4. Transfer Syntax — "어떻게 인코딩?"

이게 DICOM 호환성의 마법. 1980년대 CT 스캐너가 2026년 Windows 노트북과 통신할 수 있는 이유.

 

4.1 Transfer Syntax가 다루는 것

  • Endian (Big/Little) — 1편 6번 참조
  • VR encoding (Implicit/Explicit) — 2편 4번
  • 압축 알고리즘 (JPEG, JPEG-LS, JPEG2000, RLE)

 

4.2 주요 Transfer Syntax UID

Transfer Syntax UID 의미
Implicit VR Little Endian 1.2.840.10008.1.2 DICOM 기본값. 무조건 지원 필수
Explicit VR Little Endian 1.2.840.10008.1.2.1 Explicit 안전성
Explicit VR Big Endian 1.2.840.10008.1.2.2 구 Mac 등 (거의 사용 안 함)
JPEG Baseline 8-bit Lossy 1.2.840.10008.1.2.4.50 일반 JPEG 손실
JPEG Baseline 12-bit Lossy 1.2.840.10008.1.2.4.51 12bit 의료영상용
JPEG Lossless 1.2.840.10008.1.2.4.57 JPEG 무손실
JPEG-LS Lossless 1.2.840.10008.1.2.4.80 의료영상에서 흔함
JPEG-LS Near-Lossless 1.2.840.10008.1.2.4.81 거의 무손실
JPEG 2000 Lossless 1.2.840.10008.1.2.4.90 최신, 권장
JPEG 2000 Lossy 1.2.840.10008.1.2.4.91 손실 압축
RLE Lossless 1.2.840.10008.1.2.5 Run-Length, 단순

 

4.3 핵심 규칙

모든 DICOM AE는 1.2.840.10008.1.2 (Implicit VR LE) 를 무조건 지원해야 함.

이게 안전망. 다른 압축 협상 다 실패해도 이걸로 fallback 하면 됨.

 

4.4 인코딩 (Abstract와 거의 동일)

┌──────┬──────────┬─────────┬─────────────────────┐
│ 0x40 │ reserved │ Length  │  UID string         │
│ 1byte│ 1 byte=0 │ 2 bytes │  L bytes            │
└──────┴──────────┴─────────┴─────────────────────┘

첫 바이트가 0x40 (Transfer Syntax)인 것만 다름.

 

4.5 ⚠️ 압축 함정 — 저자 사례

한 병원 US 장비가 영상을 압축 형식으로 저장 (디스크 용량 절약).
Archive 서버는 압축 미지원.
→ US가 영상 보내려 했을 때 → Transfer Syntax 불일치로 거부.

 

문제 진단:

  1. US 기사가 자기도 모르게 압축 켬 → Transfer Syntax 바뀜
  2. US 장비 펌웨어가 "압축 거부되면 압축 안 한 거로 fallback" 안 함 → 표준 위반

 

해결:

  • 모든 AE는 거부당하면 Implicit VR LE로 fallback 해야 함
  • 또는 Archive에서 그 압축 지원 추가

 

4.6 압축 옵션 함정

Transfer Syntax는 알고리즘 이름만 협상. 품질/비율 같은 파라미터는 협상 안 됨.
→ 받는 쪽은 "JPEG2000으로 압축됐다"만 알지 "품질 80%" 같은 건 모름.
→ 그래서 의료영상은 무손실 압축 (Lossless) 선호.

 


5. Presentation Context = Abstract + Transfer 묶음 ⭐⭐

여기가 핵심. 위 두 개념이 합쳐진다.

 

5.1 정의

Presentation Context = 1개의 Abstract Syntax + 여러 개의 Transfer Syntax 후보

┌─────────────────────────────────────────────────────────┐
│  Presentation Context (PrC)                              │
│  ├── Presentation Context ID (1, 3, 5, ... 홀수)         │
│  ├── Abstract Syntax: MR Image Storage                   │
│  └── Transfer Syntax 후보들:                              │
│      ├── 1.2.840.10008.1.2 (Implicit VR LE)              │
│      ├── 1.2.840.10008.1.2.4.80 (JPEG-LS Lossless)       │
│      └── 1.2.840.10008.1.2.4.91 (JPEG 2000 Lossy)        │
└─────────────────────────────────────────────────────────┘

 

💡 비유: "이 일(Abstract)을 다음 언어들(Transfer) 중 어떤 걸로든 할 수 있어요" 라는 명함.

 

5.2 협상 과정

Calling AE → Called AE (요청)

여러 Presentation Context를 묶어서 보냄:

A-Associate-RQ
├── PrC ID=1: MR Storage / [Implicit LE, JPEG-LS, JPEG2000]
├── PrC ID=3: CT Storage / [Implicit LE]
├── PrC ID=5: Verification / [Implicit LE]
└── ...

 

Called AE → Calling AE (응답)

각 PrC에 대해 수락/거부 + 선택한 Transfer Syntax 1개

A-Associate-AC
├── PrC ID=1: ✅ Accept, Transfer Syntax = Implicit LE
├── PrC ID=3: ❌ Reject (reason 3: Abstract syntax not supported)
├── PrC ID=5: ✅ Accept, Transfer Syntax = Implicit LE
└── ...

→ PrC ID로 어떤 컨텍스트의 응답인지 매칭.

 

5.3 Acceptance Reason 코드

A-Associate-AC의 각 PrC 응답에 들어가는 "왜 수락/거부했나"

코드 의미
0 ✅ Acceptance (성공)
1 ❌ User-rejection
2 ❌ No-reason (provider)
3 Abstract syntax not supported
4 Transfer syntaxes not supported

 

🛠 PACS 통신 디버깅의 진짜 핵심:
로그에서 PrC가 거부됐다면 reason 코드 먼저 확인.

  • 3이면 → SOP Class 자체를 안 받는 거 (장비 호환성 문제)
  • 4면 → SOP는 OK지만 압축 등 인코딩 형식이 안 맞는 거 (해결 가능)

 

5.4 실제 인코딩 — 두 가지 형식

A-Associate-RQ의 PrC: 첫 바이트 0x20. Abstract 1개 + Transfer 여러 개.

A-Associate-AC의 PrC: 첫 바이트 0x21. Abstract 없음. Transfer 1개만. PrC ID로 매칭.

→ AC에 Abstract Syntax 안 들어있어서 PrC ID 매칭이 유일한 방법. ID 잘못 맞추면 망함.

 


6. Application Context (대부분 무시)

A-Associate에 들어가지만 거의 안 봄.

  • 표준 default: 1.2.840.10008.3.1.1.1
  • 거의 모든 구현이 이 default 사용
  • 이론상 "어떤 회사 소프트웨어인지" 식별 가능 — 사설 협상 트리거용
  • 실무에서는 그냥 default 보내고 받는 쪽도 무시

 

⚠️ 가끔 악용: 일부 PACS가 "경쟁사 Application Context면 거부" 한다는 루머. 대부분 그냥 무시.

 


7. User Information — 작지만 까다로운 함정

A-Associate-RQ/AC에 들어가는 잡다한 추가 정보.

 

7.1 주요 subitem

Subitem 의미 기본값
Maximum PDU Length PDU 최대 크기 (byte) 16384, 64KB 등
Implementation Class UID 구현 식별자 (벤더별)
Implementation Version Name 버전 문자열 (≤ 16자) (벤더별)
Async Operations Window 비동기 큐 사이즈 1 (= 동기)
SCP/SCU Role Selection 역할 협상 RQ측=SCU 가정
Extended Negotiation 추가 협상 (relational query 등) 없음

 

7.2 ⚠️ 저자의 함정 사례 — 두 글자 때문에 하루 날림

CR ↔ Archive 연결이 안 됨. 하루 종일 디버깅.
원인: Archive의 Implementation Version Name이 18자 ("ArchiveVersion.123").
DICOM 표준상 최대 16자. CR이 그걸 엄격히 검사해서 통째로 거부.

 

교훈:

  • User Information의 모든 길이 제한 엄수
  • 그래야 까다로운 벤더와도 호환

 

7.3 Maximum PDU Length

PDU(Protocol Data Unit) 한 번에 보낼 수 있는 최대 크기. 큰 영상은 여러 PDU로 쪼갬.

  • 너무 작으면: 영상이 잘게 쪼개져 오버헤드↑
  • 너무 크면: 메모리 부담, 네트워크 단편화

흔한 값: 16384 (16KB), 65536 (64KB), 131072 (128KB)

 

🛠 Orthanc/pynetdicom 기본값은 보통 16KB. 큰 영상 자주 주고받으면 늘려도 됨.

 

7.4 SCP/SCU Role Negotiation

기본 가정: RQ를 보낸 쪽 = SCU, 받는 쪽 = SCP.

예외: C-Get/C-Move 처럼 SCP가 SCU에게 거꾸로 C-Store 하는 경우. 이때 Role 명시 필요.

A-Associate-RQ
├── PrC: MR Storage
├── User Info:
│   └── SCP/SCU Role Selection: "나는 SCU 이자 SCP 역할 둘 다 함"

→ Calling AE가 "내가 보내준 영상을 받을 수도 있어요" 라고 알림.

 


8. PDU — Protocol Data Unit

Association 위에서 흐르는 7가지 메시지 단위.

PDU 1st Byte 역할
A-Associate-RQ 0x01 연결 요청
A-Associate-AC 0x02 연결 수락
A-Associate-RJ 0x03 연결 거부
P-Data-TF 0x04 실제 데이터 전송
A-Release-RQ 0x05 정상 종료 요청
A-Release-RP 0x06 정상 종료 응답
A-Abort 0x07 비정상 종료

 

8.1 전체 흐름도

Calling AE                    Called AE
   │                              │
   │  A-Associate-RQ (0x01)       │
   ─────────────────────────────→ │
   │                              │
   │       ┌────────────────────┐ │
   │       │ 협상 결과          │ │
   │       └────────────────────┘ │
   │                              │
   │  A-Associate-AC (0x02)       │
   ←───────────────────────────── │  ← 성공 시
   │                              │
   │      또는                    │
   │                              │
   │  A-Associate-RJ (0x03)       │
   ←───────────────────────────── │  ← 거부 시
   │                              │
   │  ─── 협상 성공한 경우 ───      │
   │                              │
   │  P-Data-TF (0x04) ↔  ↔  ↔   │
   ─────────────────────────────→ │  ← DIMSE 메시지들 흐름
   ←───────────────────────────── │
   │  ...                         │
   │                              │
   │  A-Release-RQ (0x05)         │
   ─────────────────────────────→ │
   │                              │
   │  A-Release-RP (0x06)         │
   ←───────────────────────────── │
   │                              │
   │  ─── 또는 비정상 종료 ───     │
   │                              │
   │  A-Abort (0x07)              │
   ─────────────────────────────→ │  (양방향 가능)

 

8.2 A-Associate-RQ 구조

┌────────────────────────────────────────────────────┐
│ PDU type = 0x01                                     │
│ Reserved (0x00)                                     │
│ Length (4 bytes)                                    │
│ Protocol version (2 bytes, = 0x0001)               │
│ Reserved (2 bytes)                                  │
│ Called AE Title (16 bytes, blank padded)            │
│ Calling AE Title (16 bytes, blank padded)           │
│ Reserved (32 bytes)                                 │
│                                                     │
│ ─── Variable items ─────────────────────────       │
│   Application Context Item                          │
│   Presentation Context Item(s) (여러 개 가능)        │
│   User Information Item                             │
└────────────────────────────────────────────────────┘

 

8.3 ⚠️ AE Title 16바이트 패딩

AE Title은 정확히 16바이트. 짧으면 공백(0x20) 으로 패딩.

"ORTHANC" → "ORTHANC         "  (앞 7자 + 공백 9개)

 

🛠 양쪽이 padding 처리 다르면 매칭 실패. 일부 PACS가 trailing space를 strip 안 하고 비교해서 거부하는 사례 있음. AE Title은 짧고 명확하게 (8자 이하 권장).

 

8.4 화이트리스트 검사

A-Associate-RQ 받자마자 Called AE가 하는 일:

# 의사코드
def on_associate_rq(rq):
    if rq.calling_ae_title not in self.whitelist:
        return A_Associate_RJ(reason="no reason given")  # 흔한 거부 사유
    ...

 

⚠️ 이게 4편 §2.4에서 본 "양방향 등록 함정" 의 정체. PACS의 whitelist에 AI 추론 앱 AE 등록 안 되면 무조건 거부.

 


9. A-Associate-RJ (거부)

A-Associate-RQ의 거부 응답. 단순 구조.

 

9.1 거부 메시지 필드

필드 의미
Result 1 Rejected-Permanent (영구)
  2 Rejected-Transient (일시)
Source 1 DICOM UL service-user
  2 DICOM UL service-provider (ACSE)
  3 DICOM UL service-provider (Presentation)
Reason 1 No reason given ← 가장 자주 봄
  2 Application context name not supported
  3 Calling AE Title not recognized
  4-6 (reserved)
  7 Called AE Title not recognized

 

9.2 "No reason given" 의 진실

DICOM 디버깅의 최대 적. 벤더가 정보 안 줘서 그런 거지 실제로는 명확한 이유가 있음. 가장 흔한 실제 원인

  1. Calling AE Title이 whitelist에 없음
  2. Abstract Syntax 미지원 (장비가 해당 SOP 모름)
  3. Maximum PDU Length 협상 실패
  4. Implementation Version Name 형식 위반
  5. 방화벽이 association 막음

 

🛠 저자 권고:
"No reason given" 받으면 자체 디버깅하지 말고 양쪽 벤더를 한자리에 모아라. 단독으로 X 벤더에게 물으면 Y 탓 하고, Y에게 물으면 X 탓 함.

 


10. P-Data-TF — 실제 데이터 전송

Association이 성공한 후 흐르는 데이터 PDU.

 

10.1 구조

┌──────────────────────────────────────────────────┐
│ PDU type = 0x04                                   │
│ Reserved (0x00)                                   │
│ PDU Length                                        │
│                                                   │
│ ─── PDV (Protocol Data Value) items ───          │
│   PDV Length (4 bytes)                            │
│   Presentation Context ID (1 byte)                │
│   MCH (Message Control Header, 1 byte) ↓          │
│   PDV Data (DIMSE message fragment)               │
│   ...                                             │
└──────────────────────────────────────────────────┘

 

10.2 MCH (Message Control Header) — 1바이트의 비밀

  Bit 7 6 5 4 3 2 1 0
            │   │ │ │
            │   │ │ └─ Command(1) or Data(0)?
            │   │ └─── Last fragment(1) or not(0)?
            └─── reserved
  • bit 0: 1이면 Command 객체 (group 0000), 0이면 Data 객체
  • bit 1: 1이면 이게 마지막 fragment, 0이면 더 옴

→ DIMSE 메시지를 PDU 크기에 맞게 쪼개고, 받는 쪽에서 다시 조립.

 

10.3 흐름 예시: C-Store 한 번

C-Store-Rq (Command Object, 작음)
  → 1개 PDV (MCH=0x03 = command + last)

영상 데이터 (큰 IOD, 10MB)
  → 여러 PDV로 쪼개짐:
    PDV 1: MCH=0x00 (data + 계속)
    PDV 2: MCH=0x00 (data + 계속)
    ...
    PDV N: MCH=0x02 (data + last)

→ C-Store 완료 → C-Store-Rsp 응답

 

10.4 PrC ID 매칭

각 PDV에는 Presentation Context ID 가 붙어 있음.
→ 받는 쪽이 "이 PDV는 Implicit LE로 해석해야 하나, JPEG 압축으로 해석해야 하나" 를 PrC ID로 결정.

 


11. Association 종료

11.1 정상 종료 — A-Release

SCU                            SCP
 │                              │
 │  A-Release-RQ (0x05)         │  ← 작업 끝, 끊자
 ────────────────────────────→  │
 │                              │
 │  A-Release-RP (0x06)         │  ← OK
 ←────────────────────────────  │
 │                              │
 ─── TCP 연결 종료 ───

 

11.2 비정상 종료 — A-Abort

오류 상황에서 즉시 끊기:

  • 잘못된 PDU 받음
  • 타임아웃
  • 사용자가 강제 종료
A-Abort (0x07) → 한쪽이 일방적으로 보냄 → TCP close

 

11.3 Timeout 종료

상대가 응답 없으면 일정 시간 후 자동 종료. 보통 30-60초 설정.

 

🛠 너무 짧으면 느린 네트워크에서 끊김. 너무 길면 죽은 연결 감지 늦음.

 

11.4 ⚠️ 동시 Association 수 라이선스

일부 PACS 벤더는 동시 Association 수 라이선스로 판매. 10개, 50개 등.
→ 모든 Association을 반드시 A-Release로 정상 종료 해야 슬롯 회수됨.
→ 안 그러면 슬롯이 누적되어 새 Association 받을 수 없게 됨.

 


12. Association 디버깅 — 실전 가이드 ⭐⭐⭐

저자의 노하우 + 실무 베스트 프랙티스.

 

12.1 디버깅 순서

1. C-Echo (ping)
   ├── 성공 → AE Title, IP, Port, whitelist 모두 OK → 다음 단계로
   └── 실패 → 12.2 로

2. C-Store SCU (실제 영상 전송)
   ├── 성공 → 완료
   └── 실패 → Abstract/Transfer Syntax 협상 문제 → 12.3 로

3. C-Find / C-Move (검색/가져오기)
   └── ...

 

12.2 C-Echo 실패 트러블슈팅

증상 확인
TCP timeout 1) 네트워크 케이블/방화벽
2) IP/Port 오타
3) SCP 데몬이 안 돔
Association rejected (no reason) 1) Calling AE Title이 PACS whitelist에 있나?
2) Called AE Title 오타
3) Implementation Version Name 16자 초과
Abstract syntax not supported Verification SOP SCP 미지원 (Conformance 확인)
즉시 disconnect 일부 PACS는 whitelist 미등록 시 그냥 끊음

 

12.3 C-Store 실패 트러블슈팅

증상 확인
Abstract syntax not supported SOP Class UID 확인. CT 영상 보내는데 PACS가 MR Storage만 받는 등.
Transfer syntaxes not supported 압축 형식 미지원. Implicit LE로 fallback 시도.
Out of resources PACS 디스크/메모리 부족. 관리자에게
Cannot understand IOD 형식 위반. Type 1 필드 누락 등
일부 영상만 실패 Patient ID 빈 값, 손상된 픽셀 등 개별 데이터 문제

 

12.4 로그 분석 핵심 키워드

Orthanc 로그

W [Orthanc Plugin]   Connection failed (rc = 0xc0001000, ...)
I [Orthanc Plugin]   ASSOCIATE-RQ from CT_SCANNER (10.0.0.5:11112)
I [Orthanc Plugin]   ASSOCIATE-AC sent
I [Orthanc Plugin]   P-DATA-TF: 1 PDV, ...
W [Orthanc Plugin]   Refusing presentation context (abstract syntax not supported)

 

pynetdicom 로그

import logging
logging.basicConfig(level=logging.DEBUG)
# 또는
from pynetdicom import debug_logger
debug_logger()

→ 모든 PDU/PDV 바이트 단위로 출력. RQ/AC 협상 내용 다 보임.

 

12.5 패킷 캡처 (Wireshark)

진짜 안 풀리는 경우 마지막 수단:

tshark -i any -f "port 4242" -w dicom.pcap

Wireshark는 DICOM dissector 내장. 모든 PDU/PDV/Abstract/Transfer Syntax를 트리로 보여줌. AE Title의 trailing space, PDU length 오류 같은 것도 한 번에 드러남.

 

12.6 ⚠️ 책의 황금률

  1. DICOM 협상 실패는 거의 항상 양쪽 벤더 책임. 한쪽만 잡지 말고 둘을 마주 앉혀라.
  2. 명확한 로그/에러 표시 없는 PACS는 사지 말 것. 디버깅 불가.
  3. Conformance Statement를 신뢰하되 검증하라. "지원한다"고 적힌 게 실제로는 미구현인 경우 흔함.

 


13. Point-to-Point의 한계

DICOM의 근본적 약점 하나 더.

 

13.1 정적 등록 강제

모든 AE는 통신할 상대의 (AET, IP, Port)를 미리 등록 해야 함.

  • 변경되면 모든 곳을 다시 등록해야 함.
  • 모바일/원격 접근 어려움.

 

13.2 저자의 허리케인 카트리나 사례

카트리나 후 정전 → 라우터 재부팅 → IP 주소가 공장 default로 리셋
→ PACS 서버 IP 바뀜 → 모든 AE가 PACS를 못 찾음 → 전체 마비
→ IP 복원하니 즉시 정상화

 

→ TCP/IP는 살아있는데 DICOM의 정적 등록 때문에 전체 시스템이 죽는 패러독스.

 

13.3 C-Move vs C-Get 와 연결

5편 §4.2에서 본 방화벽 함정과 같은 뿌리:

  • C-Move는 정적 등록 필수 (별도 association 열어야 하니까)
  • C-Get은 동적 가능 (같은 association으로 응답)

 

13.4 워크어라운드

방식 설명
VPN 가상 사설망으로 정적 IP처럼 보이게
DICOMweb (WADO/STOW/QIDO) HTTP REST 기반, 정적 등록 불필요
Orthanc/dcm4chee gateway 외부에서 들어온 요청을 내부 PACS로 중계

 

💡 현대 PACS는 DICOMweb 지원이 점점 표준화 중. DICOMweb은 책에 없지만 별도 학습할 가치 있음 (PS3.18).

 


14. AI 추론 앱 관점 — Orthanc 사용 시 ⭐

저수준 PDU는 Orthanc가 다 처리해줌. 하지만 설정과 디버깅에서 위 개념들이 그대로 등장.

 

14.1 Orthanc 설정 예시

// orthanc.json
{
  "DicomAet": "ORTHANC",
  "DicomPort": 4242,
  "DicomCheckCalledAet": false,
  "DicomCheckModalityHost": false,

  "DicomModalities": {
    "HOSPITAL_PACS": {
      "AET": "HOSPITAL_PACS",
      "Host": "10.0.0.50",
      "Port": 11112,
      "Manufacturer": "Generic",
      "AllowEcho": true,
      "AllowFind": true,
      "AllowGet": false,
      "AllowMove": true,
      "AllowStore": true
    }
  },

  // ⭐ Transfer Syntax 협상 제어
  "AcceptedTransferSyntaxes": [
    "1.2.840.10008.1.*"
  ],

  // ⭐ Abstract Syntax 협상 제어
  "DefaultEncoding": "Latin1",
  "MaximumPduLength": 16384,

  // ⭐ Whitelist
  "DicomAlwaysAllowEcho": true,
  "DicomAlwaysAllowStore": true
}

→ 위 설정의 거의 모든 필드가 이번 글에서 다룬 개념과 직접 대응

 

  • DicomAet = 우리 AI 추론 앱의 Calling AE Title
  • AET/Host/Port = 원격 PACS의 (AE Title, IP, Port) 사전 등록
  • AcceptedTransferSyntaxes = 협상 시 받아들일 Transfer Syntax UID 목록
  • MaximumPduLength = User Information의 Max PDU Length
  • DicomAlwaysAllowStore = whitelist 검사 우회 (개발 환경에서)

 

14.2 디버깅 명령

# C-Echo 테스트
curl -X POST http://orthanc:8042/modalities/HOSPITAL_PACS/echo

# 협상 로그 보기 (Orthanc verbose 모드)
Orthanc.exe --verbose orthanc.json
# 또는
Orthanc.exe --trace-dicom orthanc.json   # ← Association/PDU 레벨 로그

 

14.3 흔한 시나리오와 매핑

증상 원인 (이번 글의 개념) 해결
Orthanc에서 PACS로 C-Echo 실패 Whitelist 미등록 PACS에 ORTHANC AE 등록 요청
C-Store 시 RejectedPresentationContext Transfer Syntax 미협상 AcceptedTransferSyntaxes에 압축 추가
C-Move 했는데 영상 안 옴 Move Destination 등록 안 됨 PACS에 우리 AE를 destination으로 등록
Implementation 16자 오류 User Info 길이 위반 Orthanc 버전 업데이트 또는 벤더 문의
큰 영상 전송 중단 Max PDU Length MaximumPduLength 늘림 (e.g., 65536)

 


15. 핵심 정리

개념 한 줄
Association DICOM Upper Layer 통신 단위. TCP 위 핸드셰이크
Act 1/2/3 Establishment / Data Transfer / Termination
Abstract Syntax "어떤 SOP" — UID 형식. 협상 불가, 매칭만
Transfer Syntax "어떤 인코딩" — Endian/Implicit/Explicit/압축. 여러 후보 협상
기본 Transfer Syntax 1.2.840.10008.1.2 (Implicit VR LE) — 무조건 지원 필수
Presentation Context Abstract 1개 + Transfer 후보 N개. PrC ID로 추적
A-Associate-RQ/AC 협상 요청/수락 PDU
A-Associate-RJ 거부. "no reason given"이 흔함 → 실제는 whitelist 문제 다수
A-Abort 비정상 즉시 종료
A-Release-RQ/RP 정상 종료 (라이선스 슬롯 회수)
P-Data-TF 실제 데이터 PDU. PDV로 쪼갬. MCH 1바이트로 command/data + last 표시
User Information Max PDU, Version Name(16자!), Role Selection 등
PrC Reject Reason 3 Abstract Syntax 미지원 (SOP 자체 문제)
PrC Reject Reason 4 Transfer Syntax 미지원 (인코딩 문제, fallback 가능)
AE Title 16바이트, 공백 패딩. 짧고 명확하게
디버깅 순서 C-Echo → C-Store → 로그 → Wireshark
점-대-점 한계 정적 등록 강제. DICOMweb이 대안

 


16. 다음 글에서 다룰 것

DICOM 통신의 모든 기본기가 완성됐다.

 

마지막 7편은 운영 환경에서의 실전

 

7편: DICOM 파일/보안 + Orthanc 실전

  • DICOM File 포맷DICM 매직 헤더, Preamble, Meta Information
  • DICOMDIR — DVD/CD/USB 미디어 인덱스
  • 익명화 (Anonymization) — HIPAA, 환자 데이터 보호 ⭐
  • 암호화 — Secure DICOM, 디지털 서명
  • Orthanc 실전 통합 시나리오 — AI 추론 앱 전체 파이프라인
  • DICOMweb 맛보기 — WADO-RS, STOW-RS, QIDO-RS

 


참고

  • Pianykh, O.S. Digital Imaging and Communications in Medicine (DICOM). Springer, 2008.
  • 공식 표준:
    • PS3.7 (Message Exchange) — DIMSE
    • PS3.8 (Network Communication Support) — 이번 글의 핵심: PDU, Association protocol
  • 도구:
    • Wireshark — DICOM dissector 내장. 패킷 단위 분석.
    • pynetdicom — Python 구현 + DEBUG 로깅
    • Orthanc--trace-dicom 옵션으로 PDU 레벨 로그
    • DCMTK dcmnetstorescu, findscu, echoscu 등 CLI 도구