comparison Core/DicomNetworking/DicomServer.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/DicomProtocol/DicomServer.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 #include "../PrecompiledHeaders.h"
35 #include "DicomServer.h"
36
37 #include "../../Core/Logging.h"
38 #include "../../Core/MultiThreading/RunnableWorkersPool.h"
39 #include "../../Core/OrthancException.h"
40 #include "../../Core/Toolbox.h"
41 #include "Internals/CommandDispatcher.h"
42
43 #include <boost/thread.hpp>
44
45 #if defined(__linux__)
46 #include <cstdlib>
47 #endif
48
49
50 namespace Orthanc
51 {
52 struct DicomServer::PImpl
53 {
54 boost::thread thread_;
55 T_ASC_Network *network_;
56 std::auto_ptr<RunnableWorkersPool> workers_;
57 };
58
59
60 void DicomServer::ServerThread(DicomServer* server)
61 {
62 LOG(INFO) << "DICOM server started";
63
64 while (server->continue_)
65 {
66 /* receive an association and acknowledge or reject it. If the association was */
67 /* acknowledged, offer corresponding services and invoke one or more if required. */
68 std::auto_ptr<Internals::CommandDispatcher> dispatcher(Internals::AcceptAssociation(*server, server->pimpl_->network_));
69
70 try
71 {
72 if (dispatcher.get() != NULL)
73 {
74 server->pimpl_->workers_->Add(dispatcher.release());
75 }
76 }
77 catch (OrthancException& e)
78 {
79 LOG(ERROR) << "Exception in the DICOM server thread: " << e.What();
80 }
81 }
82
83 LOG(INFO) << "DICOM server stopping";
84 }
85
86
87 DicomServer::DicomServer() :
88 pimpl_(new PImpl),
89 aet_("ANY-SCP")
90 {
91 port_ = 104;
92 modalities_ = NULL;
93 findRequestHandlerFactory_ = NULL;
94 moveRequestHandlerFactory_ = NULL;
95 storeRequestHandlerFactory_ = NULL;
96 worklistRequestHandlerFactory_ = NULL;
97 applicationEntityFilter_ = NULL;
98 checkCalledAet_ = true;
99 associationTimeout_ = 30;
100 continue_ = false;
101 }
102
103 DicomServer::~DicomServer()
104 {
105 if (continue_)
106 {
107 LOG(ERROR) << "INTERNAL ERROR: DicomServer::Stop() should be invoked manually to avoid mess in the destruction order!";
108 Stop();
109 }
110 }
111
112 void DicomServer::SetPortNumber(uint16_t port)
113 {
114 Stop();
115 port_ = port;
116 }
117
118 uint16_t DicomServer::GetPortNumber() const
119 {
120 return port_;
121 }
122
123 void DicomServer::SetAssociationTimeout(uint32_t seconds)
124 {
125 LOG(INFO) << "Setting timeout for DICOM connections if Orthanc acts as SCP (server): "
126 << seconds << " seconds (0 = no timeout)";
127
128 Stop();
129 associationTimeout_ = seconds;
130 }
131
132 uint32_t DicomServer::GetAssociationTimeout() const
133 {
134 return associationTimeout_;
135 }
136
137
138 void DicomServer::SetCalledApplicationEntityTitleCheck(bool check)
139 {
140 Stop();
141 checkCalledAet_ = check;
142 }
143
144 bool DicomServer::HasCalledApplicationEntityTitleCheck() const
145 {
146 return checkCalledAet_;
147 }
148
149 void DicomServer::SetApplicationEntityTitle(const std::string& aet)
150 {
151 if (aet.size() == 0)
152 {
153 throw OrthancException(ErrorCode_BadApplicationEntityTitle);
154 }
155
156 if (aet.size() > 16)
157 {
158 throw OrthancException(ErrorCode_BadApplicationEntityTitle);
159 }
160
161 for (size_t i = 0; i < aet.size(); i++)
162 {
163 if (!(aet[i] == '-' ||
164 aet[i] == '_' ||
165 isdigit(aet[i]) ||
166 (aet[i] >= 'A' && aet[i] <= 'Z')))
167 {
168 LOG(WARNING) << "For best interoperability, only upper case, alphanumeric characters should be present in AET: \"" << aet << "\"";
169 break;
170 }
171 }
172
173 Stop();
174 aet_ = aet;
175 }
176
177 const std::string& DicomServer::GetApplicationEntityTitle() const
178 {
179 return aet_;
180 }
181
182 void DicomServer::SetRemoteModalities(IRemoteModalities& modalities)
183 {
184 Stop();
185 modalities_ = &modalities;
186 }
187
188 DicomServer::IRemoteModalities& DicomServer::GetRemoteModalities() const
189 {
190 if (modalities_ == NULL)
191 {
192 throw OrthancException(ErrorCode_BadSequenceOfCalls);
193 }
194 else
195 {
196 return *modalities_;
197 }
198 }
199
200 void DicomServer::SetFindRequestHandlerFactory(IFindRequestHandlerFactory& factory)
201 {
202 Stop();
203 findRequestHandlerFactory_ = &factory;
204 }
205
206 bool DicomServer::HasFindRequestHandlerFactory() const
207 {
208 return (findRequestHandlerFactory_ != NULL);
209 }
210
211 IFindRequestHandlerFactory& DicomServer::GetFindRequestHandlerFactory() const
212 {
213 if (HasFindRequestHandlerFactory())
214 {
215 return *findRequestHandlerFactory_;
216 }
217 else
218 {
219 throw OrthancException(ErrorCode_NoCFindHandler);
220 }
221 }
222
223 void DicomServer::SetMoveRequestHandlerFactory(IMoveRequestHandlerFactory& factory)
224 {
225 Stop();
226 moveRequestHandlerFactory_ = &factory;
227 }
228
229 bool DicomServer::HasMoveRequestHandlerFactory() const
230 {
231 return (moveRequestHandlerFactory_ != NULL);
232 }
233
234 IMoveRequestHandlerFactory& DicomServer::GetMoveRequestHandlerFactory() const
235 {
236 if (HasMoveRequestHandlerFactory())
237 {
238 return *moveRequestHandlerFactory_;
239 }
240 else
241 {
242 throw OrthancException(ErrorCode_NoCMoveHandler);
243 }
244 }
245
246 void DicomServer::SetStoreRequestHandlerFactory(IStoreRequestHandlerFactory& factory)
247 {
248 Stop();
249 storeRequestHandlerFactory_ = &factory;
250 }
251
252 bool DicomServer::HasStoreRequestHandlerFactory() const
253 {
254 return (storeRequestHandlerFactory_ != NULL);
255 }
256
257 IStoreRequestHandlerFactory& DicomServer::GetStoreRequestHandlerFactory() const
258 {
259 if (HasStoreRequestHandlerFactory())
260 {
261 return *storeRequestHandlerFactory_;
262 }
263 else
264 {
265 throw OrthancException(ErrorCode_NoCStoreHandler);
266 }
267 }
268
269 void DicomServer::SetWorklistRequestHandlerFactory(IWorklistRequestHandlerFactory& factory)
270 {
271 Stop();
272 worklistRequestHandlerFactory_ = &factory;
273 }
274
275 bool DicomServer::HasWorklistRequestHandlerFactory() const
276 {
277 return (worklistRequestHandlerFactory_ != NULL);
278 }
279
280 IWorklistRequestHandlerFactory& DicomServer::GetWorklistRequestHandlerFactory() const
281 {
282 if (HasWorklistRequestHandlerFactory())
283 {
284 return *worklistRequestHandlerFactory_;
285 }
286 else
287 {
288 throw OrthancException(ErrorCode_NoWorklistHandler);
289 }
290 }
291
292 void DicomServer::SetApplicationEntityFilter(IApplicationEntityFilter& factory)
293 {
294 Stop();
295 applicationEntityFilter_ = &factory;
296 }
297
298 bool DicomServer::HasApplicationEntityFilter() const
299 {
300 return (applicationEntityFilter_ != NULL);
301 }
302
303 IApplicationEntityFilter& DicomServer::GetApplicationEntityFilter() const
304 {
305 if (HasApplicationEntityFilter())
306 {
307 return *applicationEntityFilter_;
308 }
309 else
310 {
311 throw OrthancException(ErrorCode_NoApplicationEntityFilter);
312 }
313 }
314
315 void DicomServer::Start()
316 {
317 if (modalities_ == NULL)
318 {
319 LOG(ERROR) << "No list of modalities was provided to the DICOM server";
320 throw OrthancException(ErrorCode_BadSequenceOfCalls);
321 }
322
323 Stop();
324
325 /* initialize network, i.e. create an instance of T_ASC_Network*. */
326 OFCondition cond = ASC_initializeNetwork
327 (NET_ACCEPTOR, OFstatic_cast(int, port_), /*opt_acse_timeout*/ 30, &pimpl_->network_);
328 if (cond.bad())
329 {
330 LOG(ERROR) << "cannot create network: " << cond.text();
331 throw OrthancException(ErrorCode_DicomPortInUse);
332 }
333
334 continue_ = true;
335 pimpl_->workers_.reset(new RunnableWorkersPool(4)); // Use 4 workers - TODO as a parameter?
336 pimpl_->thread_ = boost::thread(ServerThread, this);
337 }
338
339
340 void DicomServer::Stop()
341 {
342 if (continue_)
343 {
344 continue_ = false;
345
346 if (pimpl_->thread_.joinable())
347 {
348 pimpl_->thread_.join();
349 }
350
351 pimpl_->workers_.reset(NULL);
352
353 /* drop the network, i.e. free memory of T_ASC_Network* structure. This call */
354 /* is the counterpart of ASC_initializeNetwork(...) which was called above. */
355 OFCondition cond = ASC_dropNetwork(&pimpl_->network_);
356 if (cond.bad())
357 {
358 LOG(ERROR) << "Error while dropping the network: " << cond.text();
359 }
360 }
361 }
362
363
364 bool DicomServer::IsMyAETitle(const std::string& aet) const
365 {
366 if (modalities_ == NULL)
367 {
368 throw OrthancException(ErrorCode_BadSequenceOfCalls);
369 }
370
371 if (!HasCalledApplicationEntityTitleCheck())
372 {
373 // OK, no check on the AET.
374 return true;
375 }
376 else
377 {
378 return modalities_->IsSameAETitle(aet, GetApplicationEntityTitle());
379 }
380 }
381
382 }