Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Comments on Fail to send DICOM C-Echo, although DICOM Association seems successful.

Post

Fail to send DICOM C-Echo, although DICOM Association seems successful.

+6
−0

I am building an Android app to perform a DICOM C-Echo. (DICOM is a standard for storing and transferring medical images; the specification is at dicom.nema.org). My purpose is to learn more about how DICOM works, by implementing things myself.

To test my application, I use DCMTk's storescp command, which implements the receiving party of a DICOM C-Echo. So, on my VPS, which runs Ubuntu, I give this command:

sudo storescp -d 104

(The "-d" flag is for "debug", giving me extra information).

When I use DCMTk to create a DICOM Association, I get the following response:

D: $dcmtk: storescp v3.6.2 2017-07-14 $
D:
D: DcmDataDictionary: Loading file: /usr/share/libdcmtk12/dicom.dic
D: DcmDataDictionary: Loading file: /usr/share/libdcmtk12/private.dic
D: setting network send timeout to 60 seconds
D: setting network receive timeout to 60 seconds
D: PDU Type: Associate Request, PDU Length: 205 + 6 bytes PDU header
D:   01  00  00  00  00  cd  00  01  00  00  41  4e  59  2d  53  43
D:   50  20  20  20  20  20  20  20  20  20  45  43  48  4f  53  43
D:   55  20  20  20  20  20  20  20  20  20  00  00  00  00  00  00
D:   00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
D:   00  00  00  00  00  00  00  00  00  00  10  00  00  15  31  2e
D:   32  2e  38  34  30  2e  31  30  30  30  38  2e  33  2e  31  2e
D:   31  2e  31  20  00  00  2e  01  00  ff  00  30  00  00  11  31
D:   2e  32  2e  38  34  30  2e  31  30  30  30  38  2e  31  2e  31
D:   40  00  00  11  31  2e  32  2e  38  34  30  2e  31  30  30  30
D:   38  2e  31  2e  32  50  00  00  3a  51  00  00  04  00  00  40
D:   00  52  00  00  1b  31  2e  32  2e  32  37  36  2e  30  2e  37
D:   32  33  30  30  31  30  2e  33  2e  30  2e  33  2e  36  2e  32
D:   55  00  00  0f  4f  46  46  49  53  5f  44  43  4d  54  4b  5f
D:   33  36  32
D: Parsing an A-ASSOCIATE PDU
I: Association Received
D: Parameters:
D: ====================== BEGIN A-ASSOCIATE-RQ =====================
D: Our Implementation Class UID:      1.2.276.0.7230010.3.0.3.6.2
D: Our Implementation Version Name:   OFFIS_DCMTK_362
D: Their Implementation Class UID:    1.2.276.0.7230010.3.0.3.6.2
D: Their Implementation Version Name: OFFIS_DCMTK_362
D: Application Context Name:    1.2.840.10008.3.1.1.1
D: Calling Application Name:    ECHOSCU
D: Called Application Name:     ANY-SCP
D: Responding Application Name:
D: Our Max PDU Receive Size:    16384
D: Their Max PDU Receive Size:  16384
D: Presentation Contexts:
D:   Context ID:        1 (Proposed)
D:     Abstract Syntax: =VerificationSOPClass
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax(es):
D:       =LittleEndianImplicit
D: Requested Extended Negotiation: none
D: Accepted Extended Negotiation:  none
D: Requested User Identity Negotiation: none
D: User Identity Negotiation Response:  none
D: ======================= END A-ASSOCIATE-RQ ======================
D: Constructing Associate AC PDU
I: Association Acknowledged (Max Send PDV: 16372)
D: ====================== BEGIN A-ASSOCIATE-AC =====================
D: Our Implementation Class UID:      1.2.276.0.7230010.3.0.3.6.2
D: Our Implementation Version Name:   OFFIS_DCMTK_362
D: Their Implementation Class UID:    1.2.276.0.7230010.3.0.3.6.2
D: Their Implementation Version Name: OFFIS_DCMTK_362
D: Application Context Name:    1.2.840.10008.3.1.1.1
D: Calling Application Name:    ECHOSCU
D: Called Application Name:     ANY-SCP
D: Responding Application Name: ANY-SCP
D: Our Max PDU Receive Size:    16384
D: Their Max PDU Receive Size:  16384
D: Presentation Contexts:
D:   Context ID:        1 (Accepted)
D:     Abstract Syntax: =VerificationSOPClass
D:     Proposed SCP/SCU Role: Default
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =LittleEndianImplicit
D: Requested Extended Negotiation: none
D: Accepted Extended Negotiation:  none
D: Requested User Identity Negotiation: none
D: User Identity Negotiation Response:  none
D: ======================= END A-ASSOCIATE-AC ======================
D: DcmDataset::read() TransferSyntax="Little Endian Implicit"
I: Received Echo Request
D: ===================== INCOMING DIMSE MESSAGE ====================
D: Message Type                  : C-ECHO RQ
D: Presentation Context ID       : 1
D: Message ID                    : 1
D: Data Set                      : none
D: ======================= END DIMSE MESSAGE =======================
I: Association Release

When I use my app to establish the DICOM Association, I get a similar result:

D: setting network send timeout to 60 seconds
D: setting network receive timeout to 60 seconds
D: PDU Type: Associate Request, PDU Length: 188 + 6 bytes PDU header
D:   01  00  00  00  00  bc  00  01  00  00  41  4e  59  2d  53  43
D:   50  20  20  20  20  20  20  20  20  20  45  43  48  4f  53  43
D:   55  20  20  20  20  20  20  20  20  20  00  00  00  00  00  00
D:   00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
D:   00  00  00  00  00  00  00  00  00  00  10  00  00  15  31  2e
D:   32  2e  38  34  30  2e  31  30  30  30  38  2e  33  2e  31  2e
D:   31  2e  31  20  00  00  2e  01  00  00  00  30  00  00  11  31
D:   2e  32  2e  38  34  30  2e  31  30  30  30  38  2e  31  2e  31
D:   40  00  00  11  31  2e  32  2e  38  34  30  2e  31  30  30  30
D:   38  2e  31  2e  32  50  00  00  29  51  00  00  04  00  00  40
D:   00  52  00  00  15  31  2e  32  2e  38  34  30  2e  31  30  30
D:   30  38  2e  33  2e  31  2e  31  2e  31  53  00  00  04  00  01
D:   00  01
D: Parsing an A-ASSOCIATE PDU
I: Association Received
D: Parameters:
D: ====================== BEGIN A-ASSOCIATE-RQ =====================
D: Our Implementation Class UID:      1.2.276.0.7230010.3.0.3.6.2
D: Our Implementation Version Name:   OFFIS_DCMTK_362
D: Their Implementation Class UID:    1.2.840.10008.3.1.1.1
D: Their Implementation Version Name:
D: Application Context Name:    1.2.840.10008.3.1.1.1
D: Calling Application Name:    ECHOSCU
D: Called Application Name:     ANY-SCP
D: Responding Application Name:
D: Our Max PDU Receive Size:    16384
D: Their Max PDU Receive Size:  16384
D: Presentation Contexts:
D:   Context ID:        1 (Proposed)
D:     Abstract Syntax: =VerificationSOPClass
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax(es):
D:       =LittleEndianImplicit
D: Requested Extended Negotiation: none
D: Accepted Extended Negotiation:  none
D: Requested User Identity Negotiation: none
D: User Identity Negotiation Response:  none
D: ======================= END A-ASSOCIATE-RQ ======================
D: Constructing Associate AC PDU
I: Association Acknowledged (Max Send PDV: 16372)
D: ====================== BEGIN A-ASSOCIATE-AC =====================
D: Our Implementation Class UID:      1.2.276.0.7230010.3.0.3.6.2
D: Our Implementation Version Name:   OFFIS_DCMTK_362
D: Their Implementation Class UID:    1.2.840.10008.3.1.1.1
D: Their Implementation Version Name:
D: Application Context Name:    1.2.840.10008.3.1.1.1
D: Calling Application Name:    ECHOSCU
D: Called Application Name:     ANY-SCP
D: Responding Application Name: ANY-SCP
D: Our Max PDU Receive Size:    16384
D: Their Max PDU Receive Size:  16384
D: Presentation Contexts:
D:   Context ID:        1 (Accepted)
D:     Abstract Syntax: =VerificationSOPClass
D:     Proposed SCP/SCU Role: Default
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =LittleEndianImplicit
D: Requested Extended Negotiation: none
D: Accepted Extended Negotiation:  none
D: Requested User Identity Negotiation: none
D: User Identity Negotiation Response:  none
D: ======================= END A-ASSOCIATE-AC ======================
E: DIMSE failure (aborting association): 0006:020d DIMSE Failed to receive message
E: 0006:020c DIMSE Read PDV failed
E: 0006:0321 Unrecognized PDU type:  0

So, in both cases, the response is A-ASSOCIATE-AC, meaning the DICOM Association is accepted. (The other possible responses are A-ASSOCIATE-RJ, when it is rejected, and A-ABORT, when it fails disastrously).

When I use DCMTk's echoscu to send an Echo request, things work smooth:

D: DcmDataset::read() TransferSyntax="Little Endian Implicit"
I: Received Echo Request
D: ===================== INCOMING DIMSE MESSAGE ====================
D: Message Type                  : C-ECHO RQ
D: Presentation Context ID       : 1
D: Message ID                    : 1
D: Data Set                      : none
D: ======================= END DIMSE MESSAGE =======================

However, when I use my app to send the request message, I get this:

E: DIMSE failure (aborting association): 0006:020d DIMSE Failed to receive message
E: 0006:020c DIMSE Read PDV failed
E: 0006:0321 Unrecognized PDU type:  0

I have inspected DCMTk's echoscu message using Wireshark. and it sends almost the same bytes that I send.

These are the bytes that DCMTK's echoscu sends, broken down into DICOM Elements.

00 00        g = 0000
00 00        e = 0000
04 00 00 00  VR length = 4
38 00 00 00  VR value = 0x38 = 56 dec

00 00        g=0000
02 00        e=0000
12 00 00 00  VR length = 0x12 = 18dec
31 2e 32 2e 38 34 30 2e 31 30 30 30 38 2e 31 2e 31 00 VR value = 1.2.840.10008.1.1

00 00        g=0000
00 01        e=0100
02 00 00 00  VR length = 2
30 00        VR value = 0030

00 00        g=0000
10 01        e=0110
02 00 00 00  vR length = 2
01 00        VR value = 0001

00 00        g=0000
00 08        e=0800
02 00 00 00  VR length = 2
01 01        VR Value = 0101

These are the bytes that my app sends, again broken down into DICOM elements:

00 00        g=0000
00 00        e=0000
04 00 00 00  VR length = 4  
38 00 00 00  VR value = 56 dec

00 00        g=0000
02 00        e=0002
12 00 00 00  VR length = 18
31 2e 32 2e 38 34 30 2e 31 30 30 30 38 2e 31 2e 31 00 VR value =  1.2.840.10008.1.1
                                                    
00 00        g=0000
00 01        e=0100 
02 00 00 00  VR length = 2
30 00        VR value = 0

00 00        g=0000
10 01        e=0110 
02 00 00 00  VR length = 2
01 00        VR value = 0x0001

00 00        g=0000
00 08        e=0800
02 00 00 00  VR length = 2 
01 01        VR value = 0101

So, it seems a DICOM Association was created succesfully in both cases, and in both cases a very similar series of bytes is sent. So why does the first case succeed, whereas the second case fails?

The error message suggests that I should have sent more: "Unrecognized PDU type: 0". It sounds as if I need to embed my DICOM C-Echo request in something else. Or as if storescp expects me to create a new command at the level of creating and releasing associations, rather than just a DICOM C-Echo request. In fact, when I don't send the bytes for a DICOM C-Echo but just the 10 bytes to release the Assocation (DICOM A-Release-RQ), the association is released smoothly.

So.. what am I missing?

  • Is there an error in the way I set up the DICOM Association?
  • Do I need to embed my DICOM C-Echo Request inside something else?
  • Do I need to set the receiving party (storescp) in a different mode to receive C-Echo, rather than to expect A-Associate-RQ or A-Release-RQ?

Code of the app is on my Github.

EDIT: Since I don't have Wireshark on Android, I decided to run my app from the Android emulator. As @ghost-in-the-zsh suggested, I have made sure that the bytes sent by my app are exactly the same as those sent by DCMTk's echoscu program.

If I do this, I see that Wireshark does not recognize my C-Echo bytes as DICOM. It labels them as TCP. But it does label echoscu's C-Echo bytes as DICOM.
So I'm probably just making a mistake in my networking code.

I have added screenshots of my captures, with the relevant message selected. I can add the captures themselves, if necessary.

Capture of the traffic from the Android emulator to the VPS:

WireShark capture of traffic from Android emulator to VPS

Capture of the traffic from DCMTK's echoscu to the VPS:

WireShark capture of traffic from DCMTK echoscu to VPS

History
Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

1 comment thread

General comments (13 comments)
General comments

Skipping 1 deleted comment.

ghost-in-the-zsh‭ wrote over 3 years ago · edited over 3 years ago

I'm not familiar with DICOM, but I have a few questions: Your app is not sending the exact same bytes as the successful request (e.g. VR value = 0001 that works vs your app's VR value = 0xfeca). What's the significance of the different bytes? Why are they even different if you're (apparently) trying to do the same thing? Are you able to check logs for the receiving end of your request? If so, what do you find in them? Warnings? Errors? Something else? If not, then who can check?

Moshi‭ wrote over 3 years ago

@FractionalRadix For future reference, you can create the tag yourself if it doesn't already exist :) Just add it to the tag field when you submit your question and it'll be automatically created

FractionalRadix‭ wrote over 3 years ago

@ghost-in-the-zsh Well spotted! The 0xfeca value is arbitrary; it is a value that the receiver must return, so the sender knows it got the right message. (And yes, that's not watertight... but that's the spec). There are no logs from the receiver; using the "-d" option is the most informative you can get. This is because C-ECHO is a very basic operation. The difficult part was in establishing a "DICOM Association" between the two parties. Although now I suspect the issue is in my network code.

FractionalRadix‭ wrote over 3 years ago

@Moshi ...so, I unlocked that Ability? That would be nice! I tried adding new tags myself, but it didn't work.

Moshi‭ wrote over 3 years ago

@FractionalRadix That's odd, tag creation isn't currently ability-locked. Perhaps raise a bug report?

ghost-in-the-zsh‭ wrote over 3 years ago · edited over 3 years ago

@FractionalRadix Have you tried using the values from the working request in the failing request? How did you confirm that they're really arbitrary and not meaningful in some way? One of the first things I'd try is sending an exact copy of the working message/packet thru the app just to rule that out. Yes, I'd definitely trace what your network code is doing to make sure the bytes you're displaying are being sent exactly as shown. Also, remember that network code is always big-endian!

FractionalRadix‭ wrote over 3 years ago

@Moshi Well, now apparently I can create new tags... I guess I missed something. Thanks for pointing out that it's not ability-locked!

Skipping 1 deleted comment.

FractionalRadix‭ wrote over 3 years ago

@ghost-in-the-zsh As you suggested, I've tried again with exactly the same bytes. I've included screenshots of the captures. If necessary, I could add the .pcapng files, I guess. (Sorry for getting back late BTW; this app is a hobby project, and I have little spare time to work on it.) Good point about network code always being big endian! DICOM supports big endian, but defaults to little endian. Probably for historical reasons; that's usually the case with DICOM.

ghost-in-the-zsh‭ wrote over 3 years ago

@FractionalRadix: I took a brief look at your network code, but it was a bit difficult to follow - especially your 'byte' manipulations in Strings, etc. I think if you spend some time cleaning up and simplifying the code, it might be easier to verify for correctness. Also, in the screenshots, the working packets show request ECHOSCU --> ANY-SCP while the non-working one shows request ECHOSCU --> ECHOSCP. Is that expected?

ghost-in-the-zsh‭ wrote over 3 years ago

Why not ANY-SCP like the first or at least ECHOSCU? Why ECHOSCP? In the non-working one, things seem "ok" until packet 8, which causes the remote end to give you an ABORT ECHOSCU --> ECHOSCP. Do ECHOSCP and ANY-SCP have the same message sequence? Is this the right sequence for ECHOSCP?

wimh‭ wrote over 3 years ago

In the capture of the traffic from DCMTK's echoscu to the VPS, the data is 12+68=80 bytes split over 2 tcp packets (packet #8+#10). Your capture is just a single tcp packet of 68 bytes, so 12 bytes seem to be missing. It is clear from the wireshark screenshot, that those additional bytes are required to decode the data. You should be able to expand the correct package to see exactly what's inside. Your screenshot shows only the summary (DICOM, C-ECHO-RQ ID=1)

ghost-in-the-zsh‭ wrote about 3 years ago · edited about 3 years ago

It'd be nice to see the author answer their own question - especially if they managed to figure out and fix the problem.

FractionalRadix‭ wrote about 3 years ago

@ghost-in-the-zsh If I find the solution, I'll post it. This was always a hobby project, and I've been busy with other ones.