comparison OrthancServer/DicomProtocol/DicomUserConnection.cpp @ 930:27d256e0b458 mac

integration mainline -> mac
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 24 Jun 2014 16:47:18 +0200
parents 55b945749bed 816dccaeb7cf
children b3f6fb1130cd
comparison
equal deleted inserted replaced
928:882833632b1f 930:27d256e0b458
28 * You should have received a copy of the GNU General Public License 28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <http://www.gnu.org/licenses/>. 29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 **/ 30 **/
31 31
32 32
33
34 /*=========================================================================
35
36 This file is based on portions of the following project:
37
38 Program: DCMTK 3.6.0
39 Module: http://dicom.offis.de/dcmtk.php.en
40
41 Copyright (C) 1994-2011, OFFIS e.V.
42 All rights reserved.
43
44 This software and supporting documentation were developed by
45
46 OFFIS e.V.
47 R&D Division Health
48 Escherweg 2
49 26121 Oldenburg, Germany
50
51 Redistribution and use in source and binary forms, with or without
52 modification, are permitted provided that the following conditions
53 are met:
54
55 - Redistributions of source code must retain the above copyright
56 notice, this list of conditions and the following disclaimer.
57
58 - Redistributions in binary form must reproduce the above copyright
59 notice, this list of conditions and the following disclaimer in the
60 documentation and/or other materials provided with the distribution.
61
62 - Neither the name of OFFIS nor the names of its contributors may be
63 used to endorse or promote products derived from this software
64 without specific prior written permission.
65
66 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
67 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
68 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
69 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
70 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
71 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
72 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
73 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
74 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
75 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
76 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
77
78 =========================================================================*/
79
80
81 #include "../PrecompiledHeadersServer.h"
33 #include "DicomUserConnection.h" 82 #include "DicomUserConnection.h"
34 83
35 #include "../../Core/OrthancException.h" 84 #include "../../Core/OrthancException.h"
36 #include "../ToDcmtkBridge.h" 85 #include "../ToDcmtkBridge.h"
37 #include "../FromDcmtkBridge.h" 86 #include "../FromDcmtkBridge.h"
57 #define HOST_NAME_MAX 256 106 #define HOST_NAME_MAX 256
58 #endif 107 #endif
59 108
60 109
61 #if !defined(HOST_NAME_MAX) && defined(_POSIX_HOST_NAME_MAX) 110 #if !defined(HOST_NAME_MAX) && defined(_POSIX_HOST_NAME_MAX)
111 /**
112 * TO IMPROVE: "_POSIX_HOST_NAME_MAX is only the minimum value that
113 * HOST_NAME_MAX can ever have [...] Therefore you cannot allocate an
114 * array of size _POSIX_HOST_NAME_MAX, invoke gethostname() and expect
115 * that the result will fit."
116 * http://lists.gnu.org/archive/html/bug-gnulib/2009-08/msg00128.html
117 **/
62 #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX 118 #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
63 #endif 119 #endif
64 120
65 121
66 static const char* DEFAULT_PREFERRED_TRANSFER_SYNTAX = UID_LittleEndianImplicitTransferSyntax; 122 static const char* DEFAULT_PREFERRED_TRANSFER_SYNTAX = UID_LittleEndianImplicitTransferSyntax;
123
124 /**
125 * "If we have more than 64 storage SOP classes, tools such as
126 * storescu will fail because they attempt to negotiate two
127 * presentation contexts for each SOP class, and there is a total
128 * limit of 128 contexts for one association."
129 **/
130 static const unsigned int MAXIMUM_STORAGE_SOP_CLASSES = 64;
131
67 132
68 namespace Orthanc 133 namespace Orthanc
69 { 134 {
70 struct DicomUserConnection::PImpl 135 struct DicomUserConnection::PImpl
71 { 136 {
106 { 171 {
107 pimpl_->CheckIsOpen(); 172 pimpl_->CheckIsOpen();
108 } 173 }
109 174
110 175
111 void DicomUserConnection::CopyParameters(const DicomUserConnection& other) 176 static void RegisterStorageSOPClass(T_ASC_Parameters* params,
112 { 177 unsigned int& presentationContextId,
113 Close(); 178 const std::string& sopClass,
114 localAet_ = other.localAet_; 179 const char* asPreferred[],
115 distantAet_ = other.distantAet_; 180 std::vector<const char*>& asFallback)
116 distantHost_ = other.distantHost_; 181 {
117 distantPort_ = other.distantPort_; 182 Check(ASC_addPresentationContext(params, presentationContextId,
118 manufacturer_ = other.manufacturer_; 183 sopClass.c_str(), asPreferred, 1));
119 preferredTransferSyntax_ = other.preferredTransferSyntax_; 184 presentationContextId += 2;
120 } 185
121 186 if (asFallback.size() > 0)
122 187 {
188 Check(ASC_addPresentationContext(params, presentationContextId,
189 sopClass.c_str(), &asFallback[0], asFallback.size()));
190 presentationContextId += 2;
191 }
192 }
193
194
123 void DicomUserConnection::SetupPresentationContexts(const std::string& preferredTransferSyntax) 195 void DicomUserConnection::SetupPresentationContexts(const std::string& preferredTransferSyntax)
124 { 196 {
125 // Fallback transfer syntaxes 197 // Flatten an array with the preferred transfer syntax
198 const char* asPreferred[1] = { preferredTransferSyntax.c_str() };
199
200 // Setup the fallback transfer syntaxes
126 std::set<std::string> fallbackSyntaxes; 201 std::set<std::string> fallbackSyntaxes;
127 fallbackSyntaxes.insert(UID_LittleEndianExplicitTransferSyntax); 202 fallbackSyntaxes.insert(UID_LittleEndianExplicitTransferSyntax);
128 fallbackSyntaxes.insert(UID_BigEndianExplicitTransferSyntax); 203 fallbackSyntaxes.insert(UID_BigEndianExplicitTransferSyntax);
129 fallbackSyntaxes.insert(UID_LittleEndianImplicitTransferSyntax); 204 fallbackSyntaxes.insert(UID_LittleEndianImplicitTransferSyntax);
130
131 // Transfer syntaxes for C-ECHO, C-FIND and C-MOVE
132 std::vector<std::string> transferSyntaxes;
133 transferSyntaxes.push_back(UID_VerificationSOPClass);
134 transferSyntaxes.push_back(UID_FINDPatientRootQueryRetrieveInformationModel);
135 transferSyntaxes.push_back(UID_FINDStudyRootQueryRetrieveInformationModel);
136 transferSyntaxes.push_back(UID_MOVEStudyRootQueryRetrieveInformationModel);
137
138 // TODO: Allow the set below to be configured
139 std::set<std::string> uselessSyntaxes;
140 uselessSyntaxes.insert(UID_BlendingSoftcopyPresentationStateStorage);
141 uselessSyntaxes.insert(UID_GrayscaleSoftcopyPresentationStateStorage);
142 uselessSyntaxes.insert(UID_ColorSoftcopyPresentationStateStorage);
143 uselessSyntaxes.insert(UID_PseudoColorSoftcopyPresentationStateStorage);
144
145 // Add the transfer syntaxes for C-STORE
146 for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs - 1; i++)
147 {
148 // Test to make some room to allow the ECHO and FIND requests
149 if (uselessSyntaxes.find(dcmShortSCUStorageSOPClassUIDs[i]) == uselessSyntaxes.end())
150 {
151 transferSyntaxes.push_back(dcmShortSCUStorageSOPClassUIDs[i]);
152 }
153 }
154
155 // Flatten the fallback transfer syntaxes array
156 const char* asPreferred[1] = { preferredTransferSyntax.c_str() };
157
158 fallbackSyntaxes.erase(preferredTransferSyntax); 205 fallbackSyntaxes.erase(preferredTransferSyntax);
159 206
207 // Flatten an array with the fallback transfer syntaxes
160 std::vector<const char*> asFallback; 208 std::vector<const char*> asFallback;
161 asFallback.reserve(fallbackSyntaxes.size()); 209 asFallback.reserve(fallbackSyntaxes.size());
162 for (std::set<std::string>::const_iterator 210 for (std::set<std::string>::const_iterator
163 it = fallbackSyntaxes.begin(); it != fallbackSyntaxes.end(); ++it) 211 it = fallbackSyntaxes.begin(); it != fallbackSyntaxes.end(); ++it)
164 { 212 {
165 asFallback.push_back(it->c_str()); 213 asFallback.push_back(it->c_str());
166 } 214 }
167 215
216 CheckStorageSOPClassesInvariant();
168 unsigned int presentationContextId = 1; 217 unsigned int presentationContextId = 1;
169 for (size_t i = 0; i < transferSyntaxes.size(); i++) 218
170 { 219 for (std::list<std::string>::const_iterator it = reservedStorageSOPClasses_.begin();
171 Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, 220 it != reservedStorageSOPClasses_.end(); it++)
172 transferSyntaxes[i].c_str(), asPreferred, 1)); 221 {
173 presentationContextId += 2; 222 RegisterStorageSOPClass(pimpl_->params_, presentationContextId,
174 223 *it, asPreferred, asFallback);
175 if (asFallback.size() > 0) 224 }
176 { 225
177 Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, 226 for (std::set<std::string>::const_iterator it = storageSOPClasses_.begin();
178 transferSyntaxes[i].c_str(), &asFallback[0], asFallback.size())); 227 it != storageSOPClasses_.end(); it++)
179 presentationContextId += 2; 228 {
180 } 229 RegisterStorageSOPClass(pimpl_->params_, presentationContextId,
230 *it, asPreferred, asFallback);
231 }
232
233 for (std::set<std::string>::const_iterator it = defaultStorageSOPClasses_.begin();
234 it != defaultStorageSOPClasses_.end(); it++)
235 {
236 RegisterStorageSOPClass(pimpl_->params_, presentationContextId,
237 *it, asPreferred, asFallback);
181 } 238 }
182 } 239 }
183 240
184 241
185 static bool IsGenericTransferSyntax(const std::string& syntax) 242 static bool IsGenericTransferSyntax(const std::string& syntax)
195 CheckIsOpen(); 252 CheckIsOpen();
196 253
197 DcmFileFormat dcmff; 254 DcmFileFormat dcmff;
198 Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength)); 255 Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength));
199 256
257 // Determine the storage SOP class UID for this instance
258 static const DcmTagKey DCM_SOP_CLASS_UID(0x0008, 0x0016);
259 OFString sopClassUid;
260 if (dcmff.getDataset()->findAndGetOFString(DCM_SOP_CLASS_UID, sopClassUid).good())
261 {
262 connection.AddStorageSOPClass(sopClassUid.c_str());
263 }
264
200 // Determine whether a new presentation context must be 265 // Determine whether a new presentation context must be
201 // negociated, depending on the transfer syntax of this instance 266 // negotiated, depending on the transfer syntax of this instance
202 DcmXfer xfer(dcmff.getDataset()->getOriginalXfer()); 267 DcmXfer xfer(dcmff.getDataset()->getOriginalXfer());
203 const std::string syntax(xfer.getXferID()); 268 const std::string syntax(xfer.getXferID());
204 bool isGeneric = IsGenericTransferSyntax(syntax); 269 bool isGeneric = IsGenericTransferSyntax(syntax);
205 270
206 if (isGeneric ^ IsGenericTransferSyntax(connection.GetPreferredTransferSyntax())) 271 if (isGeneric ^ IsGenericTransferSyntax(connection.GetPreferredTransferSyntax()))
207 { 272 {
208 // Making a generic-to-specific or specific-to-generic change of 273 // Making a generic-to-specific or specific-to-generic change of
209 // the transfer syntax. Renegociate the connection. 274 // the transfer syntax. Renegotiate the connection.
210 LOG(INFO) << "Renegociating a C-Store association due to a change in the transfer syntax"; 275 LOG(INFO) << "Change in the transfer syntax: the C-Store associated must be renegotiated";
211 276
212 if (isGeneric) 277 if (isGeneric)
213 { 278 {
214 connection.ResetPreferredTransferSyntax(); 279 connection.ResetPreferredTransferSyntax();
215 } 280 }
216 else 281 else
217 { 282 {
218 connection.SetPreferredTransferSyntax(syntax); 283 connection.SetPreferredTransferSyntax(syntax);
219 } 284 }
220 285 }
286
287 if (!connection.IsOpen())
288 {
289 LOG(INFO) << "Renegotiating a C-Store association due to a change in the parameters";
221 connection.Open(); 290 connection.Open();
222 } 291 }
223 292
224 // Figure out which SOP class and SOP instance is encapsulated in the file 293 // Figure out which SOP class and SOP instance is encapsulated in the file
225 DIC_UI sopClass; 294 DIC_UI sopClass;
235 { 304 {
236 const char *modalityName = dcmSOPClassUIDToModality(sopClass); 305 const char *modalityName = dcmSOPClassUIDToModality(sopClass);
237 if (!modalityName) modalityName = dcmFindNameOfUID(sopClass); 306 if (!modalityName) modalityName = dcmFindNameOfUID(sopClass);
238 if (!modalityName) modalityName = "unknown SOP class"; 307 if (!modalityName) modalityName = "unknown SOP class";
239 throw OrthancException("DicomUserConnection: No presentation context for modality " + 308 throw OrthancException("DicomUserConnection: No presentation context for modality " +
240 std::string(modalityName)); 309 std::string(modalityName));
241 } 310 }
242 311
243 // Prepare the transmission of data 312 // Prepare the transmission of data
244 T_DIMSE_C_StoreRQ req; 313 T_DIMSE_C_StoreRQ req;
245 memset(&req, 0, sizeof(req)); 314 memset(&req, 0, sizeof(req));
291 360
292 const char* sopClass; 361 const char* sopClass;
293 std::auto_ptr<DcmDataset> dataset(ToDcmtkBridge::Convert(fields)); 362 std::auto_ptr<DcmDataset> dataset(ToDcmtkBridge::Convert(fields));
294 switch (model) 363 switch (model)
295 { 364 {
296 case FindRootModel_Patient: 365 case FindRootModel_Patient:
297 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "PATIENT"); 366 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "PATIENT");
298 sopClass = UID_FINDPatientRootQueryRetrieveInformationModel; 367 sopClass = UID_FINDPatientRootQueryRetrieveInformationModel;
299 368
300 // Accession number 369 // Accession number
301 if (!fields.HasTag(0x0008, 0x0050)) 370 if (!fields.HasTag(0x0008, 0x0050))
302 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); 371 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), "");
303 372
304 // Patient ID 373 // Patient ID
305 if (!fields.HasTag(0x0010, 0x0020)) 374 if (!fields.HasTag(0x0010, 0x0020))
306 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0010, 0x0020), ""); 375 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0010, 0x0020), "");
307 376
308 break; 377 break;
309 378
310 case FindRootModel_Study: 379 case FindRootModel_Study:
311 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "STUDY"); 380 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "STUDY");
312 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; 381 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel;
313 382
314 // Accession number 383 // Accession number
315 if (!fields.HasTag(0x0008, 0x0050)) 384 if (!fields.HasTag(0x0008, 0x0050))
316 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); 385 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), "");
317 386
318 // Study instance UID 387 // Study instance UID
319 if (!fields.HasTag(0x0020, 0x000d)) 388 if (!fields.HasTag(0x0020, 0x000d))
320 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); 389 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), "");
321 390
322 break; 391 break;
323 392
324 case FindRootModel_Series: 393 case FindRootModel_Series:
325 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "SERIES"); 394 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "SERIES");
326 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; 395 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel;
327 396
328 // Accession number 397 // Accession number
329 if (!fields.HasTag(0x0008, 0x0050)) 398 if (!fields.HasTag(0x0008, 0x0050))
330 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); 399 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), "");
331 400
332 // Study instance UID 401 // Study instance UID
333 if (!fields.HasTag(0x0020, 0x000d)) 402 if (!fields.HasTag(0x0020, 0x000d))
334 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); 403 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), "");
335 404
336 // Series instance UID 405 // Series instance UID
337 if (!fields.HasTag(0x0020, 0x000e)) 406 if (!fields.HasTag(0x0020, 0x000e))
338 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), ""); 407 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), "");
339 408
340 break; 409 break;
341 410
342 case FindRootModel_Instance: 411 case FindRootModel_Instance:
343 if (manufacturer_ == ModalityManufacturer_ClearCanvas || 412 if (manufacturer_ == ModalityManufacturer_ClearCanvas ||
344 manufacturer_ == ModalityManufacturer_Dcm4Chee) 413 manufacturer_ == ModalityManufacturer_Dcm4Chee)
345 { 414 {
346 // This is a particular case for ClearCanvas, thanks to Peter Somlo <peter.somlo@gmail.com>. 415 // This is a particular case for ClearCanvas, thanks to Peter Somlo <peter.somlo@gmail.com>.
347 // https://groups.google.com/d/msg/orthanc-users/j-6C3MAVwiw/iolB9hclom8J 416 // https://groups.google.com/d/msg/orthanc-users/j-6C3MAVwiw/iolB9hclom8J
348 // http://www.clearcanvas.ca/Home/Community/OldForums/tabid/526/aff/11/aft/14670/afv/topic/Default.aspx 417 // http://www.clearcanvas.ca/Home/Community/OldForums/tabid/526/aff/11/aft/14670/afv/topic/Default.aspx
349 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "IMAGE"); 418 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "IMAGE");
350 } 419 }
351 else 420 else
352 { 421 {
353 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "INSTANCE"); 422 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "INSTANCE");
354 } 423 }
355 424
356 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; 425 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel;
357 426
358 // Accession number 427 // Accession number
359 if (!fields.HasTag(0x0008, 0x0050)) 428 if (!fields.HasTag(0x0008, 0x0050))
360 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); 429 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), "");
361 430
362 // Study instance UID 431 // Study instance UID
363 if (!fields.HasTag(0x0020, 0x000d)) 432 if (!fields.HasTag(0x0020, 0x000d))
364 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); 433 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), "");
365 434
366 // Series instance UID 435 // Series instance UID
367 if (!fields.HasTag(0x0020, 0x000e)) 436 if (!fields.HasTag(0x0020, 0x000e))
368 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), ""); 437 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), "");
369 438
370 // SOP Instance UID 439 // SOP Instance UID
371 if (!fields.HasTag(0x0008, 0x0018)) 440 if (!fields.HasTag(0x0008, 0x0018))
372 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0018), ""); 441 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0018), "");
373 442
374 break; 443 break;
375 444
376 default: 445 default:
377 throw OrthancException(ErrorCode_ParameterOutOfRange); 446 throw OrthancException(ErrorCode_ParameterOutOfRange);
378 } 447 }
379 448
380 // Figure out which of the accepted presentation contexts should be used 449 // Figure out which of the accepted presentation contexts should be used
381 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); 450 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass);
382 if (presID == 0) 451 if (presID == 0)
504 573
505 Check(cond); 574 Check(cond);
506 } 575 }
507 576
508 577
578 void DicomUserConnection::ResetStorageSOPClasses()
579 {
580 CheckStorageSOPClassesInvariant();
581
582 storageSOPClasses_.clear();
583 defaultStorageSOPClasses_.clear();
584
585 // Copy the short list of storage SOP classes from DCMTK, making
586 // room for the 4 SOP classes reserved for C-ECHO, C-FIND, C-MOVE.
587
588 std::set<std::string> uncommon;
589 uncommon.insert(UID_BlendingSoftcopyPresentationStateStorage);
590 uncommon.insert(UID_GrayscaleSoftcopyPresentationStateStorage);
591 uncommon.insert(UID_ColorSoftcopyPresentationStateStorage);
592 uncommon.insert(UID_PseudoColorSoftcopyPresentationStateStorage);
593
594 // Add the storage syntaxes for C-STORE
595 for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs - 1; i++)
596 {
597 if (uncommon.find(dcmShortSCUStorageSOPClassUIDs[i]) == uncommon.end())
598 {
599 defaultStorageSOPClasses_.insert(dcmShortSCUStorageSOPClassUIDs[i]);
600 }
601 }
602
603 CheckStorageSOPClassesInvariant();
604 }
605
606
509 DicomUserConnection::DicomUserConnection() : 607 DicomUserConnection::DicomUserConnection() :
510 pimpl_(new PImpl), 608 pimpl_(new PImpl),
511 preferredTransferSyntax_(DEFAULT_PREFERRED_TRANSFER_SYNTAX), 609 preferredTransferSyntax_(DEFAULT_PREFERRED_TRANSFER_SYNTAX),
512 localAet_("STORESCU"), 610 localAet_("STORESCU"),
513 distantAet_("ANY-SCP"), 611 distantAet_("ANY-SCP"),
517 manufacturer_ = ModalityManufacturer_Generic; 615 manufacturer_ = ModalityManufacturer_Generic;
518 616
519 pimpl_->net_ = NULL; 617 pimpl_->net_ = NULL;
520 pimpl_->params_ = NULL; 618 pimpl_->params_ = NULL;
521 pimpl_->assoc_ = NULL; 619 pimpl_->assoc_ = NULL;
620
621 // SOP classes for C-ECHO, C-FIND and C-MOVE
622 reservedStorageSOPClasses_.push_back(UID_VerificationSOPClass);
623 reservedStorageSOPClasses_.push_back(UID_FINDPatientRootQueryRetrieveInformationModel);
624 reservedStorageSOPClasses_.push_back(UID_FINDStudyRootQueryRetrieveInformationModel);
625 reservedStorageSOPClasses_.push_back(UID_MOVEStudyRootQueryRetrieveInformationModel);
626
627 ResetStorageSOPClasses();
522 } 628 }
523 629
524 DicomUserConnection::~DicomUserConnection() 630 DicomUserConnection::~DicomUserConnection()
525 { 631 {
526 Close(); 632 Close();
527 } 633 }
634
635
636 void DicomUserConnection::Connect(const RemoteModalityParameters& parameters)
637 {
638 SetDistantApplicationEntityTitle(parameters.GetApplicationEntityTitle());
639 SetDistantHost(parameters.GetHost());
640 SetDistantPort(parameters.GetPort());
641 SetDistantManufacturer(parameters.GetManufacturer());
642 }
643
528 644
529 void DicomUserConnection::SetLocalApplicationEntityTitle(const std::string& aet) 645 void DicomUserConnection::SetLocalApplicationEntityTitle(const std::string& aet)
530 { 646 {
531 if (localAet_ != aet) 647 if (localAet_ != aet)
532 { 648 {
597 { 713 {
598 // Don't reopen the connection 714 // Don't reopen the connection
599 return; 715 return;
600 } 716 }
601 717
718 LOG(INFO) << "Opening a DICOM SCU connection from AET \"" << GetLocalApplicationEntityTitle()
719 << "\" to AET \"" << GetDistantApplicationEntityTitle() << "\" on host "
720 << GetDistantHost() << ":" << GetDistantPort()
721 << " (manufacturer: " << EnumerationToString(GetDistantManufacturer()) << ")";
722
602 Check(ASC_initializeNetwork(NET_REQUESTOR, 0, /*opt_acse_timeout*/ 30, &pimpl_->net_)); 723 Check(ASC_initializeNetwork(NET_REQUESTOR, 0, /*opt_acse_timeout*/ 30, &pimpl_->net_));
603 Check(ASC_createAssociationParameters(&pimpl_->params_, /*opt_maxReceivePDULength*/ ASC_DEFAULTMAXPDU)); 724 Check(ASC_createAssociationParameters(&pimpl_->params_, /*opt_maxReceivePDULength*/ ASC_DEFAULTMAXPDU));
604 725
605 // Set this application's title and the called application's title in the params 726 // Set this application's title and the called application's title in the params
606 Check(ASC_setAPTitles(pimpl_->params_, localAet_.c_str(), distantAet_.c_str(), NULL)); 727 Check(ASC_setAPTitles(pimpl_->params_, localAet_.c_str(), distantAet_.c_str(), NULL));
610 gethostname(localHost, HOST_NAME_MAX - 1); 731 gethostname(localHost, HOST_NAME_MAX - 1);
611 732
612 char distantHostAndPort[HOST_NAME_MAX]; 733 char distantHostAndPort[HOST_NAME_MAX];
613 734
614 #ifdef _MSC_VER 735 #ifdef _MSC_VER
615 _snprintf 736 _snprintf
616 #else 737 #else
617 snprintf 738 snprintf
618 #endif 739 #endif
619 (distantHostAndPort, HOST_NAME_MAX - 1, "%s:%d", distantHost_.c_str(), distantPort_); 740 (distantHostAndPort, HOST_NAME_MAX - 1, "%s:%d", distantHost_.c_str(), distantPort_);
620 741
621 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, distantHostAndPort)); 742 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, distantHostAndPort));
622 743
623 // Set various options 744 // Set various options
624 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false)); 745 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false));
745 void DicomUserConnection::SetConnectionTimeout(uint32_t seconds) 866 void DicomUserConnection::SetConnectionTimeout(uint32_t seconds)
746 { 867 {
747 dcmConnectionTimeout.set(seconds); 868 dcmConnectionTimeout.set(seconds);
748 } 869 }
749 870
871
872 void DicomUserConnection::CheckStorageSOPClassesInvariant() const
873 {
874 assert(storageSOPClasses_.size() +
875 defaultStorageSOPClasses_.size() +
876 reservedStorageSOPClasses_.size() <= MAXIMUM_STORAGE_SOP_CLASSES);
877 }
878
879 void DicomUserConnection::AddStorageSOPClass(const char* sop)
880 {
881 CheckStorageSOPClassesInvariant();
882
883 if (storageSOPClasses_.find(sop) != storageSOPClasses_.end())
884 {
885 // This storage SOP class is already explicitly registered. Do
886 // nothing.
887 return;
888 }
889
890 if (defaultStorageSOPClasses_.find(sop) != defaultStorageSOPClasses_.end())
891 {
892 // This storage SOP class is not explicitly registered, but is
893 // used by default. Just register it explicitly.
894 defaultStorageSOPClasses_.erase(sop);
895 storageSOPClasses_.insert(sop);
896
897 CheckStorageSOPClassesInvariant();
898 return;
899 }
900
901 // This storage SOP class is neither explicitly, nor implicitly
902 // registered. Close the connection and register it explicitly.
903
904 Close();
905
906 if (reservedStorageSOPClasses_.size() +
907 storageSOPClasses_.size() >= MAXIMUM_STORAGE_SOP_CLASSES) // (*)
908 {
909 // The maximum number of SOP classes is reached
910 ResetStorageSOPClasses();
911 defaultStorageSOPClasses_.erase(sop);
912 }
913 else if (reservedStorageSOPClasses_.size() + storageSOPClasses_.size() +
914 defaultStorageSOPClasses_.size() >= MAXIMUM_STORAGE_SOP_CLASSES)
915 {
916 // Make room in the default storage syntaxes
917 assert(defaultStorageSOPClasses_.size() > 0); // Necessarily true because condition (*) is false
918 defaultStorageSOPClasses_.erase(*defaultStorageSOPClasses_.rbegin());
919 }
920
921 // Explicitly register the new storage syntax
922 storageSOPClasses_.insert(sop);
923
924 CheckStorageSOPClassesInvariant();
925 }
926
750 } 927 }