comparison Core/DicomNetworking/Internals/GetScp.cpp @ 3818:4f78da5613a1 c-get

Add C-GET SCP support
author Stacy Loesch <stacy.loesch@varian.com>
date Fri, 27 Mar 2020 10:06:58 -0400
parents
children 620e87e9e816
comparison
equal deleted inserted replaced
3815:c81ac6ff232b 3818:4f78da5613a1
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2019 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * In addition, as a special exception, the copyright holders of this
13 * program give permission to link the code of its release with the
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it
15 * that use the same license as the "OpenSSL" library), and distribute
16 * the linked executables. You must obey the GNU General Public License
17 * in all respects for all of the code used other than "OpenSSL". If you
18 * modify file(s) with this exception, you may extend this exception to
19 * your version of the file(s), but you are not obligated to do so. If
20 * you do not wish to do so, delete this exception statement from your
21 * version. If you delete this exception statement from all source files
22 * in the program, then also delete it here.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 **/
32
33
34
35
36 /*=========================================================================
37
38 This file is based on portions of the following project:
39
40 Program: DCMTK 3.6.0
41 Module: http://dicom.offis.de/dcmtk.php.en
42
43 Copyright (C) 1994-2011, OFFIS e.V.
44 All rights reserved.
45
46 This software and supporting documentation were developed by
47
48 OFFIS e.V.
49 R&D Division Health
50 Escherweg 2
51 26121 Oldenburg, Germany
52
53 Redistribution and use in source and binary forms, with or without
54 modification, are permitted provided that the following conditions
55 are met:
56
57 - Redistributions of source code must retain the above copyright
58 notice, this list of conditions and the following disclaimer.
59
60 - Redistributions in binary form must reproduce the above copyright
61 notice, this list of conditions and the following disclaimer in the
62 documentation and/or other materials provided with the distribution.
63
64 - Neither the name of OFFIS nor the names of its contributors may be
65 used to endorse or promote products derived from this software
66 without specific prior written permission.
67
68 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
69 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
70 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
71 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
72 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
73 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
74 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
75 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
76 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
77 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
78 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
79
80 =========================================================================*/
81
82
83 #include "../../PrecompiledHeaders.h"
84 #include <dcmtk/dcmnet/diutil.h>
85 #include <dcmtk/dcmdata/dcdeftag.h>
86 #include "GetScp.h"
87
88 #include <memory>
89
90 #include "../../DicomParsing/FromDcmtkBridge.h"
91 #include "../../DicomParsing/ToDcmtkBridge.h"
92 #include "../../Logging.h"
93 #include "../../OrthancException.h"
94
95 #include <boost/lexical_cast.hpp>
96
97 /**
98 * Macro specifying whether to apply the patch suggested in issue 66:
99 * "Orthanc responses C-GET with zero Get Originator Message ID"
100 * https://bitbucket.org/sjodogne/orthanc/issues/66/
101 **/
102
103 namespace Orthanc
104 {
105 namespace
106 {
107 struct GetScpData
108 {
109 // Handle returns void.
110 IGetRequestHandler* handler_;
111 DcmDataset* lastRequest_;
112 T_ASC_Association * assoc_;
113
114 std::string remoteIp_;
115 std::string remoteAet_;
116 std::string calledAet_;
117
118 GetScpData()
119 {
120 handler_ = NULL;
121 lastRequest_ = NULL;
122 assoc_ = NULL;
123 };
124 };
125
126 void BuildFailedInstanceList(const char* failedUIDs, DcmDataset ** rspIds)
127 {
128 OFBool ok;
129
130 if (failedUIDs != NULL)
131 {
132 *rspIds = new DcmDataset();
133 ok = DU_putStringDOElement(*rspIds, DCM_FailedSOPInstanceUIDList, failedUIDs);
134 if (!ok)
135 {
136 LOG (ERROR) << "getSCP: failed to build DCM_FailedSOPInstanceUIDList";
137 }
138
139 }
140 }
141
142 void GetScpCallback(
143 /* in */
144 void *callbackData,
145 OFBool cancelled,
146 T_DIMSE_C_GetRQ *request,
147 DcmDataset *requestIdentifiers,
148 int responseCount,
149 /* out */
150 T_DIMSE_C_GetRSP *response,
151 DcmDataset **responseIdentifiers,
152 DcmDataset **statusDetail)
153 {
154 bzero(response, sizeof(T_DIMSE_C_GetRSP));
155 *statusDetail = NULL;
156 *responseIdentifiers = NULL;
157
158 GetScpData& data = *reinterpret_cast<GetScpData*>(callbackData);
159 if (data.lastRequest_ == NULL)
160 {
161 DicomMap input;
162 FromDcmtkBridge::ExtractDicomSummary(input, *requestIdentifiers);
163
164 try
165 {
166 if(!data.handler_->Handle(input, data.remoteIp_, data.remoteAet_,
167 data.calledAet_))
168 {
169 response->DimseStatus = STATUS_GET_Failed_UnableToProcess;
170 return;
171 }
172 }
173 catch (OrthancException& e)
174 {
175 // Internal error!
176 LOG(ERROR) << "IGetRequestHandler Failed: " << e.What();
177 response->DimseStatus = STATUS_GET_Failed_UnableToProcess;
178 return;
179 }
180
181 data.lastRequest_ = requestIdentifiers;
182 }
183 else if (data.lastRequest_ != requestIdentifiers)
184 {
185 // Internal error!
186 LOG(ERROR) << "IGetRequestHandler Failed: Internal error lastRequestIdentifier";
187 response->DimseStatus = STATUS_GET_Failed_UnableToProcess;
188 return;
189 }
190
191 if (data.handler_->nRemaining() == 0)
192 {
193 response->DimseStatus = STATUS_Success;
194 }
195 else
196 {
197 IGetRequestHandler::Status status;
198
199 try
200 {
201 status = data.handler_->DoNext(data.assoc_);
202 }
203 catch (OrthancException& e)
204 {
205 // Internal error!
206 LOG(ERROR) << "IGetRequestHandler Failed: " << e.What();
207 response->DimseStatus = STATUS_GET_Failed_UnableToProcess;
208 return;
209 }
210
211 if (status == STATUS_Success)
212 {
213 if (responseCount < static_cast<int>(data.handler_->nRemaining()))
214 {
215 response->DimseStatus = STATUS_Pending;
216 }
217 else
218 {
219 response->DimseStatus = STATUS_Success;
220 }
221 }
222 else
223 {
224 response->DimseStatus = STATUS_GET_Failed_UnableToProcess;
225
226 if (data.handler_->nFailed() > 0 || data.handler_->warningCount() > 0)
227 {
228 response->DimseStatus = STATUS_GET_Warning_SubOperationsCompleteOneOrMoreFailures;
229 }
230 /*
231 * if all the sub-operations failed then we need to generate a failed or refused status.
232 * cf. DICOM part 4, C.4.3.3.1
233 * we choose to generate a "Refused - Out of Resources - Unable to perform suboperations" status.
234 */
235 if ((data.handler_->nFailed() > 0) && ((data.handler_->nCompleted() + data.handler_->warningCount()) == 0))
236 {
237 response->DimseStatus = STATUS_GET_Refused_OutOfResourcesSubOperations;
238 }
239
240 BuildFailedInstanceList(data.handler_->failedUids(), responseIdentifiers);
241 }
242 }
243
244 response->NumberOfRemainingSubOperations = data.handler_->nRemaining();
245 response->NumberOfCompletedSubOperations = data.handler_->nCompleted();
246 response->NumberOfFailedSubOperations = data.handler_->nFailed();
247 response->NumberOfWarningSubOperations = data.handler_->warningCount();
248 }
249
250 }
251
252 OFCondition Internals::getScp(T_ASC_Association * assoc,
253 T_DIMSE_Message * msg,
254 T_ASC_PresentationContextID presID,
255 IGetRequestHandler& handler,
256 std::string remoteIp,
257 std::string remoteAet,
258 std::string calledAet)
259 {
260
261 GetScpData data;
262 data.lastRequest_ = NULL;
263 data.handler_ = &handler;
264 data.assoc_ = assoc;
265 data.remoteIp_ = remoteIp;
266 data.remoteAet_ = remoteAet;
267 data.calledAet_ = calledAet;
268
269
270 OFCondition cond = DIMSE_getProvider(assoc, presID, &msg->msg.CGetRQ,
271 GetScpCallback, &data,
272 /*opt_blockMode*/ DIMSE_BLOCKING,
273 /*opt_dimse_timeout*/ 0);
274
275 // if some error occured, dump corresponding information and remove the outfile if necessary
276 if (cond.bad())
277 {
278 OFString temp_str;
279 LOG(ERROR) << "Get SCP Failed: " << cond.text();
280 }
281
282 return cond;
283 }
284 }