Bug 131

Summary: Orthanc PACS silently fails to C-MOVE due to duplicate StudyInstanceUID in it's database.
Product: Orthanc Reporter: Sébastien Jodogne <s.jodogne>
Component: Orthanc CoreAssignee: Sébastien Jodogne <s.jodogne>
Status: RESOLVED FIXED    
Severity: minor    
Priority: ---    
Version: unspecified   
Hardware: All   
OS: All   
Attachments: dupes.zip

Description Sébastien Jodogne 2020-06-29 15:15:13 CEST
[BitBucket user: Ales Kozumplik]
[BitBucket date: 2019-02-01.13:27:47]

* Have a pair of Orthanc servers knowing of each other's AET, call one PACS, the other one PEER
* get a sample DICOM study
* create a modified study for different PatientID, but with the same StudyInstanceUID
* upload both studies to the Orthanc PACS using POST /instances (this should probably fail but does not)
* initiate C-MOVE of either study to the PEER instance via PEER HTTP API. This seems to succeed but fails silently without moving any data over. PACS instance shows:

IMoveRequestHandler Failed: Bad request 

Note that the HTTP request (POST /queries/5e9573d4-391f-4c9f-a52b-dfb250323b4b/retrieve, with body PEER) responds with 200.

This can also be reproduced via the WEB UI Query/Retrieve.
Comment 1 Sébastien Jodogne 2020-06-29 15:17:44 CEST
Created attachment 52 [details]
dupes.zip
Comment 2 Sébastien Jodogne 2020-06-29 15:24:56 CEST
[BitBucket user: Ales Kozumplik]
[BitBucket date: 2019-02-11.11:09:45]

Debugged this further today by inspecting query-series of the original query. Each of the two patients should have exactly one study but even when querying for one of the patients, both are matched. To illustrate, notice the search was for a patient ID but the matched series belong to different patients:

    --> POST http://localhost:8042/modalities/pacs/query {"Level":"Study", "Query": {"PatientID": "99900001"}}
    <-- 200 {
       "ID" : "b83a1701-916a-472d-9b19-8e3923de0b45",
       "Path" : "/queries/b83a1701-916a-472d-9b19-8e3923de0b45"
    }
    
    --> GET http://localhost:8042//queries/b83a1701-916a-472d-9b19-8e3923de0b45/answers/0 None
    <-- 200 [ "content", "retrieve", "query-series", "query-instances" ]
    
    --> POST http://localhost:8042//queries/b83a1701-916a-472d-9b19-8e3923de0b45/answers/0/query-series {}
    <-- 200 {
       "ID" : "589931a1-7df7-4ae5-becf-2816bbc2f1cf",
       "Path" : "/queries/589931a1-7df7-4ae5-becf-2816bbc2f1cf"
    }
    
    --> GET http://localhost:8042//queries/589931a1-7df7-4ae5-becf-2816bbc2f1cf/answers None
    <-- 200 [ "0", "1" ]
    
    --> GET http://localhost:8042//queries/589931a1-7df7-4ae5-becf-2816bbc2f1cf/answers/0/content None
    <-- 200 {
       "0008,0005" : {
          "Name" : "SpecificCharacterSet",
          "Type" : "String",
          "Value" : "ISO_IR 100"
       },
       "0008,0050" : {
          "Name" : "AccessionNumber",
          "Type" : "String",
          "Value" : ""
       },
       "0008,0052" : {
          "Name" : "QueryRetrieveLevel",
          "Type" : "String",
          "Value" : "SERIES"
       },
       "0010,0020" : {
          "Name" : "PatientID",
          "Type" : "String",
          "Value" : "12345678"
       },
       "0020,000d" : {
          "Name" : "StudyInstanceUID",
          "Type" : "String",
          "Value" : "...64.4"
       },
       "0020,000e" : {
          "Name" : "SeriesInstanceUID",
          "Type" : "String",
          "Value" : "...29.9"
       }
    }
    
    --> GET http://localhost:8042//queries/589931a1-7df7-4ae5-becf-2816bbc2f1cf/answers/1/content None
    <-- 200 {
       "0008,0005" : {
          "Name" : "SpecificCharacterSet",
          "Type" : "String",
          "Value" : "ISO_IR 100"
       },
       "0008,0050" : {
          "Name" : "AccessionNumber",
          "Type" : "String",
          "Value" : ""
       },
       "0008,0052" : {
          "Name" : "QueryRetrieveLevel",
          "Type" : "String",
          "Value" : "SERIES"
       },
       "0010,0020" : {
          "Name" : "PatientID",
          "Type" : "String",
          "Value" : "99900001"
       },
       "0020,000d" : {
          "Name" : "StudyInstanceUID",
          "Type" : "String",
          "Value" : "...64.4"
       },
       "0020,000e" : {
          "Name" : "SeriesInstanceUID",
          "Type" : "String",
          "Value" : "...29.9"
       }
    }
Comment 3 Sébastien Jodogne 2020-06-29 15:24:57 CEST
[BitBucket user: Sébastien Jodogne]
[BitBucket date: 2019-02-11.15:50:27]

Please could you provide sample DICOM files, possibly anonymized, to reproduce your issue? TIA.
Comment 4 Sébastien Jodogne 2020-06-29 15:24:58 CEST
[BitBucket user: Ales Kozumplik]
[BitBucket date: 2019-02-11.16:40:06]

Hi Sebastien, I can reproduce the problem using the DICOMs attached. Each folder in the archive is one patient.
Comment 5 Sébastien Jodogne 2020-06-29 15:25:04 CEST
[BitBucket user: Sébastien Jodogne]
[BitBucket date: 2019-02-24.07:51:21]

Fix issue #131 (C-MOVE failure due to duplicate StudyInstanceUID in the database)

→ https://hg.orthanc-server.com/orthanc/changeset/6ce10c3b1eb7
Comment 6 Sébastien Jodogne 2020-06-29 15:25:22 CEST
[BitBucket user: Ales Kozumplik]
[BitBucket date: 2019-05-10.16:40:01]

Hi Sebastian, testing this on 1.5.6 the problem still exists.
Comment 7 Sébastien Jodogne 2020-06-29 15:25:23 CEST
[BitBucket user: Sébastien Jodogne]
[BitBucket date: 2019-05-14.14:04:05]

From my understanding of your issue, it is solved in Orthanc 1.5.6. 

\(1\) Let’s create 2 configuration files:

```
$ mkdir /tmp/i
$ cd /tmp/i
$ cat << EOF > a.json
{
  "DicomAet" : "PEER",
  "DicomPort" : 4242,
  "HttpPort" : 8042,
  "DicomModalities" : {
    "pacs" : [ "PACS", "localhost", 4343 ]
  },
  "StorageDirectory" : "a"
}
EOF
$ cat << EOF > b.json
{
  "DicomAet" : "PACS",
  "DicomPort" : 4343,
  "HttpPort" : 8043,
  "DicomModalities" : {
    "peer" : [ "PEER", "localhost", 4242 ]
  },
  "StorageDirectory" : "b"
}
EOF
```

\(2\) Let’s start Orthanc on those 2 configuration files in two separate terminals:

```
$ cd /tmp/i
$ ~/orthanc/Build/Orthanc a.json --verbose
```

```
$ cd /tmp/i
$ ~/orthanc/Build/Orthanc b.json --verbose
```

\(3\) Let's upload the content of your `dupes.zip` sample to Orthanc “a”:

```
$ ~/orthanc/Resources/Samples/ImportDicomFiles/ImportDicomFiles.py localhost 8043 /tmp/dupes/
```

\(4\) Check that Orthanc “a” is empty, but that Orthanc “b” contains your two instances:

```
$ curl http://localhost:8042/instances/
[]
$ curl http://localhost:8043/instances/
[
   "669397d9-6fe43fe9-c96967ee-a5a954c7-d2d8a5f8",
   "dbae414e-ae8edea3-b83daad1-648d14e9-9714a24b"
]
```

\(5\) Make Orthanc “a” \(on port 8042\) drive a query/retrieve from Orthanc “b”:

```
$ curl http://localhost:8042/modalities/pacs/query -d '{"Level":"Study", "Query": {"PatientID": "3339910"}}'
{
   "ID" : "876ee677-2397-46a3-b813-545c790eb35f",
   "Path" : "/queries/876ee677-2397-46a3-b813-545c790eb35f"
}
$ curl 'http://localhost:8042/queries/876ee677-2397-46a3-b813-545c790eb35f/answers?expand&simplify'
[
   {
      "AccessionNumber" : "B7360694",
      "PatientID" : "3339910",
      "QueryRetrieveLevel" : "STUDY",
      "SpecificCharacterSet" : "ISO_IR 100",
      "StudyInstanceUID" : "1.2.276.0.7230010.3.1.4.3942190661.3348.1548665123.1020.3339910"
   }
]
$ curl http://localhost:8042/queries/876ee677-2397-46a3-b813-545c790eb35f/retrieve -d 'PEER'
{
   "Description" : "REST API",
   "LocalAet" : "PEER",
   "Query" : [
      {
         "0008,0050" : "B7360694",
         "0008,0052" : "STUDY",
         "0010,0020" : "3339910",
         "0020,000d" : "1.2.276.0.7230010.3.1.4.3942190661.3348.1548665123.1020.3339910"
      }
   ],
   "RemoteAet" : "PACS"
}
```

\(6\) Check that Orthanc "a" has correctly received the 2 instances of `dupes.zip`:

```
$ curl http://localhost:8042/instances/
[
   "669397d9-6fe43fe9-c96967ee-a5a954c7-d2d8a5f8",
   "dbae414e-ae8edea3-b83daad1-648d14e9-9714a24b"
]
```

From my point of view, this is a correct behavior in the presence of ill-conditioned DICOM data.
Comment 8 Sébastien Jodogne 2020-06-29 15:25:24 CEST
[BitBucket user: Ales Kozumplik]
[BitBucket date: 2019-05-15.09:59:29]

Sebastian, thanks for following up. Does that mean that both the retrieving and sending instance need to be on Orthanc 1.5.6? I can still reproduce the error with an old “PACS side” but a 1.5.6 receiving side.
Comment 9 Sébastien Jodogne 2020-06-29 15:25:25 CEST
[BitBucket user: Sébastien Jodogne]
[BitBucket date: 2019-05-15.10:07:13]

Your issue was present in the C-MOVE SCP \(not in the C-STORE SCU\). In other words, your “sending Orthanc” \(aka. “PACS-side”\) must be at version >= 1.5.6 to benefit from the fix. The version of the “receiving Orthanc” \(aka. “PEER”\) has no importance.
Comment 10 Sébastien Jodogne 2020-06-29 15:25:26 CEST
[BitBucket user: Ales Kozumplik]
[BitBucket date: 2019-05-15.10:56:44]

Thanks for clarifying!