comparison Core/DicomNetworking/Internals/MoveScp.cpp @ 2382:7284093111b0

big reorganization to cleanly separate framework vs. server
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 29 Aug 2017 21:17:35 +0200
parents OrthancServer/Internals/MoveScp.cpp@b8969010b534
children 878b59270859
comparison
equal deleted inserted replaced
2381:b8969010b534 2382:7284093111b0
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 Osimis, 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 "MoveScp.h"
85
86 #include <memory>
87
88 #include "../../DicomParsing/FromDcmtkBridge.h"
89 #include "../../DicomParsing/ToDcmtkBridge.h"
90 #include "../../Logging.h"
91 #include "../../OrthancException.h"
92
93 #include <boost/lexical_cast.hpp>
94
95
96 namespace Orthanc
97 {
98 namespace
99 {
100 struct MoveScpData
101 {
102 std::string target_;
103 IMoveRequestHandler* handler_;
104 DcmDataset* lastRequest_;
105 unsigned int subOperationCount_;
106 unsigned int failureCount_;
107 unsigned int warningCount_;
108 std::auto_ptr<IMoveRequestIterator> iterator_;
109 const std::string* remoteIp_;
110 const std::string* remoteAet_;
111 const std::string* calledAet_;
112 };
113
114
115
116 static uint16_t GetMessageId(const DicomMap& message)
117 {
118 /**
119 * Retrieve the Message ID (0000,0110) for this C-MOVE request, if
120 * any. If present, this Message ID will be stored in the Move
121 * Originator Message ID (0000,1031) field of the C-MOVE response.
122 * http://dicom.nema.org/dicom/2013/output/chtml/part07/chapter_E.html
123 **/
124
125 const DicomValue* value = message.TestAndGetValue(DICOM_TAG_MESSAGE_ID);
126
127 if (value != NULL &&
128 !value->IsNull() &&
129 !value->IsBinary())
130 {
131 try
132 {
133 int tmp = boost::lexical_cast<int>(value->GetContent());
134 if (tmp >= 0 && tmp <= 0xffff)
135 {
136 return static_cast<uint16_t>(tmp);
137 }
138 }
139 catch (boost::bad_lexical_cast&)
140 {
141 LOG(WARNING) << "Cannot convert the Message ID (\"" << value->GetContent()
142 << "\") of an incoming C-MOVE request to an integer, assuming zero";
143 }
144 }
145
146 return 0;
147 }
148
149
150
151 void MoveScpCallback(
152 /* in */
153 void *callbackData,
154 OFBool cancelled,
155 T_DIMSE_C_MoveRQ *request,
156 DcmDataset *requestIdentifiers,
157 int responseCount,
158 /* out */
159 T_DIMSE_C_MoveRSP *response,
160 DcmDataset **responseIdentifiers,
161 DcmDataset **statusDetail)
162 {
163 bzero(response, sizeof(T_DIMSE_C_MoveRSP));
164 *statusDetail = NULL;
165 *responseIdentifiers = NULL;
166
167 MoveScpData& data = *reinterpret_cast<MoveScpData*>(callbackData);
168 if (data.lastRequest_ == NULL)
169 {
170 DicomMap input;
171 FromDcmtkBridge::ExtractDicomSummary(input, *requestIdentifiers);
172
173 try
174 {
175 data.iterator_.reset(data.handler_->Handle(data.target_, input, *data.remoteIp_, *data.remoteAet_,
176 *data.calledAet_, GetMessageId(input)));
177
178 if (data.iterator_.get() == NULL)
179 {
180 // Internal error!
181 response->DimseStatus = STATUS_MOVE_Failed_UnableToProcess;
182 return;
183 }
184
185 data.subOperationCount_ = data.iterator_->GetSubOperationCount();
186 data.failureCount_ = 0;
187 data.warningCount_ = 0;
188 }
189 catch (OrthancException& e)
190 {
191 // Internal error!
192 LOG(ERROR) << "IMoveRequestHandler Failed: " << e.What();
193 response->DimseStatus = STATUS_MOVE_Failed_UnableToProcess;
194 return;
195 }
196
197 data.lastRequest_ = requestIdentifiers;
198 }
199 else if (data.lastRequest_ != requestIdentifiers)
200 {
201 // Internal error!
202 response->DimseStatus = STATUS_MOVE_Failed_UnableToProcess;
203 return;
204 }
205
206 if (data.subOperationCount_ == 0)
207 {
208 response->DimseStatus = STATUS_Success;
209 }
210 else
211 {
212 IMoveRequestIterator::Status status;
213
214 try
215 {
216 status = data.iterator_->DoNext();
217 }
218 catch (OrthancException& e)
219 {
220 // Internal error!
221 LOG(ERROR) << "IMoveRequestHandler Failed: " << e.What();
222 response->DimseStatus = STATUS_MOVE_Failed_UnableToProcess;
223 return;
224 }
225
226 if (status == IMoveRequestIterator::Status_Failure)
227 {
228 data.failureCount_++;
229 }
230 else if (status == IMoveRequestIterator::Status_Warning)
231 {
232 data.warningCount_++;
233 }
234
235 if (responseCount < static_cast<int>(data.subOperationCount_))
236 {
237 response->DimseStatus = STATUS_Pending;
238 }
239 else
240 {
241 response->DimseStatus = STATUS_Success;
242 }
243 }
244
245 response->NumberOfRemainingSubOperations = data.subOperationCount_ - responseCount;
246 response->NumberOfCompletedSubOperations = responseCount;
247 response->NumberOfFailedSubOperations = data.failureCount_;
248 response->NumberOfWarningSubOperations = data.warningCount_;
249 }
250 }
251
252
253 OFCondition Internals::moveScp(T_ASC_Association * assoc,
254 T_DIMSE_Message * msg,
255 T_ASC_PresentationContextID presID,
256 IMoveRequestHandler& handler,
257 const std::string& remoteIp,
258 const std::string& remoteAet,
259 const std::string& calledAet)
260 {
261 MoveScpData data;
262 data.target_ = std::string(msg->msg.CMoveRQ.MoveDestination);
263 data.lastRequest_ = NULL;
264 data.handler_ = &handler;
265 data.remoteIp_ = &remoteIp;
266 data.remoteAet_ = &remoteAet;
267 data.calledAet_ = &calledAet;
268
269 OFCondition cond = DIMSE_moveProvider(assoc, presID, &msg->msg.CMoveRQ,
270 MoveScpCallback, &data,
271 /*opt_blockMode*/ DIMSE_BLOCKING,
272 /*opt_dimse_timeout*/ 0);
273
274 // if some error occured, dump corresponding information and remove the outfile if necessary
275 if (cond.bad())
276 {
277 OFString temp_str;
278 LOG(ERROR) << "Move SCP Failed: " << cond.text();
279 }
280
281 return cond;
282 }
283 }