comparison OrthancFramework/Sources/DicomNetworking/Internals/GetScp.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents Core/DicomNetworking/Internals/GetScp.cpp@4d1dcdf5c57e
children bf7b9edf6b81
comparison
equal deleted inserted replaced
4043:6c6239aec462 4044:d25f4c0fa160
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-2020 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 namespace Orthanc
99 {
100 namespace
101 {
102 struct GetScpData
103 {
104 // Handle returns void.
105 IGetRequestHandler* handler_;
106 DcmDataset* lastRequest_;
107 T_ASC_Association * assoc_;
108
109 std::string remoteIp_;
110 std::string remoteAet_;
111 std::string calledAet_;
112 int timeout_;
113
114 GetScpData()
115 {
116 handler_ = NULL;
117 lastRequest_ = NULL;
118 assoc_ = NULL;
119 };
120 };
121
122 static DcmDataset *BuildFailedInstanceList(const std::string& failedUIDs)
123 {
124 if (failedUIDs.empty())
125 {
126 return NULL;
127 }
128 else
129 {
130 std::unique_ptr<DcmDataset> rspIds(new DcmDataset());
131
132 if (!DU_putStringDOElement(rspIds.get(), DCM_FailedSOPInstanceUIDList, failedUIDs.c_str()))
133 {
134 throw OrthancException(ErrorCode_InternalError,
135 "getSCP: failed to build DCM_FailedSOPInstanceUIDList");
136 }
137
138 return rspIds.release();
139 }
140 }
141
142 static 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(
167 input, data.remoteIp_, data.remoteAet_, data.calledAet_,
168 data.timeout_ < 0 ? 0 : static_cast<uint32_t>(data.timeout_)))
169 {
170 response->DimseStatus = STATUS_GET_Failed_UnableToProcess;
171 return;
172 }
173 }
174 catch (OrthancException& e)
175 {
176 // Internal error!
177 LOG(ERROR) << "IGetRequestHandler Failed: " << e.What();
178 response->DimseStatus = STATUS_GET_Failed_UnableToProcess;
179 return;
180 }
181
182 data.lastRequest_ = requestIdentifiers;
183 }
184 else if (data.lastRequest_ != requestIdentifiers)
185 {
186 // Internal error!
187 LOG(ERROR) << "IGetRequestHandler Failed: Internal error lastRequestIdentifier";
188 response->DimseStatus = STATUS_GET_Failed_UnableToProcess;
189 return;
190 }
191
192 if (data.handler_->GetRemainingCount() == 0)
193 {
194 response->DimseStatus = STATUS_Success;
195 }
196 else
197 {
198 IGetRequestHandler::Status status;
199
200 try
201 {
202 status = data.handler_->DoNext(data.assoc_);
203 }
204 catch (OrthancException& e)
205 {
206 // Internal error!
207 LOG(ERROR) << "IGetRequestHandler Failed: " << e.What();
208 response->DimseStatus = STATUS_GET_Failed_UnableToProcess;
209 return;
210 }
211
212 if (status == STATUS_Success)
213 {
214 if (responseCount < static_cast<int>(data.handler_->GetRemainingCount()))
215 {
216 response->DimseStatus = STATUS_Pending;
217 }
218 else
219 {
220 response->DimseStatus = STATUS_Success;
221 }
222 }
223 else
224 {
225 response->DimseStatus = STATUS_GET_Failed_UnableToProcess;
226
227 if (data.handler_->GetFailedCount() > 0 ||
228 data.handler_->GetWarningCount() > 0)
229 {
230 response->DimseStatus = STATUS_GET_Warning_SubOperationsCompleteOneOrMoreFailures;
231 }
232
233 /*
234 * if all the sub-operations failed then we need to generate
235 * a failed or refused status. cf. DICOM part 4, C.4.3.3.1
236 * we choose to generate a "Refused - Out of Resources -
237 * Unable to perform suboperations" status.
238 */
239 if ((data.handler_->GetFailedCount() > 0) &&
240 ((data.handler_->GetCompletedCount() +
241 data.handler_->GetWarningCount()) == 0))
242 {
243 response->DimseStatus = STATUS_GET_Refused_OutOfResourcesSubOperations;
244 }
245
246 *responseIdentifiers = BuildFailedInstanceList(data.handler_->GetFailedUids());
247 }
248 }
249
250 response->NumberOfRemainingSubOperations = data.handler_->GetRemainingCount();
251 response->NumberOfCompletedSubOperations = data.handler_->GetCompletedCount();
252 response->NumberOfFailedSubOperations = data.handler_->GetFailedCount();
253 response->NumberOfWarningSubOperations = data.handler_->GetWarningCount();
254 }
255 }
256
257 OFCondition Internals::getScp(T_ASC_Association * assoc,
258 T_DIMSE_Message * msg,
259 T_ASC_PresentationContextID presID,
260 IGetRequestHandler& handler,
261 std::string remoteIp,
262 std::string remoteAet,
263 std::string calledAet,
264 int timeout)
265 {
266 GetScpData data;
267 data.lastRequest_ = NULL;
268 data.handler_ = &handler;
269 data.assoc_ = assoc;
270 data.remoteIp_ = remoteIp;
271 data.remoteAet_ = remoteAet;
272 data.calledAet_ = calledAet;
273 data.timeout_ = timeout;
274
275 OFCondition cond = DIMSE_getProvider(assoc, presID, &msg->msg.CGetRQ,
276 GetScpCallback, &data,
277 /*opt_blockMode*/ (timeout ? DIMSE_NONBLOCKING : DIMSE_BLOCKING),
278 /*opt_dimse_timeout*/ timeout);
279
280 // if some error occured, dump corresponding information and remove the outfile if necessary
281 if (cond.bad())
282 {
283 OFString temp_str;
284 LOG(ERROR) << "Get SCP Failed: " << cond.text();
285 }
286
287 return cond;
288 }
289 }