Mercurial > hg > orthanc
comparison OrthancServer/DicomProtocol/DicomUserConnection.cpp @ 763:b5e6d2823115
dynamic negotiation of storage sop classes
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 29 Apr 2014 17:15:59 +0200 |
parents | b79eda29896d |
children | 0a2f8c707c78 |
comparison
equal
deleted
inserted
replaced
760:b2a62f22fbe8 | 763:b5e6d2823115 |
---|---|
58 #endif | 58 #endif |
59 | 59 |
60 | 60 |
61 static const char* DEFAULT_PREFERRED_TRANSFER_SYNTAX = UID_LittleEndianImplicitTransferSyntax; | 61 static const char* DEFAULT_PREFERRED_TRANSFER_SYNTAX = UID_LittleEndianImplicitTransferSyntax; |
62 | 62 |
63 /** | |
64 * "If we have more than 64 storage SOP classes, tools such as | |
65 * storescu will fail because they attempt to negotiate two | |
66 * presentation contexts for each SOP class, and there is a total | |
67 * limit of 128 contexts for one association." | |
68 **/ | |
69 static const unsigned int MAXIMUM_STORAGE_SOP_CLASSES = 64; | |
70 | |
71 | |
63 namespace Orthanc | 72 namespace Orthanc |
64 { | 73 { |
65 struct DicomUserConnection::PImpl | 74 struct DicomUserConnection::PImpl |
66 { | 75 { |
67 // Connection state | 76 // Connection state |
101 { | 110 { |
102 pimpl_->CheckIsOpen(); | 111 pimpl_->CheckIsOpen(); |
103 } | 112 } |
104 | 113 |
105 | 114 |
106 void DicomUserConnection::CopyParameters(const DicomUserConnection& other) | 115 static void RegisterStorageSOPClass(T_ASC_Parameters* params, |
107 { | 116 unsigned int& presentationContextId, |
108 Close(); | 117 const std::string& sopClass, |
109 localAet_ = other.localAet_; | 118 const char* asPreferred[], |
110 distantAet_ = other.distantAet_; | 119 std::vector<const char*>& asFallback) |
111 distantHost_ = other.distantHost_; | 120 { |
112 distantPort_ = other.distantPort_; | 121 Check(ASC_addPresentationContext(params, presentationContextId, |
113 manufacturer_ = other.manufacturer_; | 122 sopClass.c_str(), asPreferred, 1)); |
114 preferredTransferSyntax_ = other.preferredTransferSyntax_; | 123 presentationContextId += 2; |
115 } | 124 |
116 | 125 if (asFallback.size() > 0) |
117 | 126 { |
127 Check(ASC_addPresentationContext(params, presentationContextId, | |
128 sopClass.c_str(), &asFallback[0], asFallback.size())); | |
129 presentationContextId += 2; | |
130 } | |
131 } | |
132 | |
133 | |
118 void DicomUserConnection::SetupPresentationContexts(const std::string& preferredTransferSyntax) | 134 void DicomUserConnection::SetupPresentationContexts(const std::string& preferredTransferSyntax) |
119 { | 135 { |
120 // Fallback transfer syntaxes | 136 // Flatten an array with the preferred transfer syntax |
137 const char* asPreferred[1] = { preferredTransferSyntax.c_str() }; | |
138 | |
139 // Setup the fallback transfer syntaxes | |
121 std::set<std::string> fallbackSyntaxes; | 140 std::set<std::string> fallbackSyntaxes; |
122 fallbackSyntaxes.insert(UID_LittleEndianExplicitTransferSyntax); | 141 fallbackSyntaxes.insert(UID_LittleEndianExplicitTransferSyntax); |
123 fallbackSyntaxes.insert(UID_BigEndianExplicitTransferSyntax); | 142 fallbackSyntaxes.insert(UID_BigEndianExplicitTransferSyntax); |
124 fallbackSyntaxes.insert(UID_LittleEndianImplicitTransferSyntax); | 143 fallbackSyntaxes.insert(UID_LittleEndianImplicitTransferSyntax); |
125 | |
126 // Transfer syntaxes for C-ECHO, C-FIND and C-MOVE | |
127 std::vector<std::string> transferSyntaxes; | |
128 transferSyntaxes.push_back(UID_VerificationSOPClass); | |
129 transferSyntaxes.push_back(UID_FINDPatientRootQueryRetrieveInformationModel); | |
130 transferSyntaxes.push_back(UID_FINDStudyRootQueryRetrieveInformationModel); | |
131 transferSyntaxes.push_back(UID_MOVEStudyRootQueryRetrieveInformationModel); | |
132 | |
133 // TODO: Allow the set below to be configured | |
134 std::set<std::string> uselessSyntaxes; | |
135 uselessSyntaxes.insert(UID_BlendingSoftcopyPresentationStateStorage); | |
136 uselessSyntaxes.insert(UID_GrayscaleSoftcopyPresentationStateStorage); | |
137 uselessSyntaxes.insert(UID_ColorSoftcopyPresentationStateStorage); | |
138 uselessSyntaxes.insert(UID_PseudoColorSoftcopyPresentationStateStorage); | |
139 | |
140 // Add the transfer syntaxes for C-STORE | |
141 for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs - 1; i++) | |
142 { | |
143 // Test to make some room to allow the ECHO and FIND requests | |
144 if (uselessSyntaxes.find(dcmShortSCUStorageSOPClassUIDs[i]) == uselessSyntaxes.end()) | |
145 { | |
146 transferSyntaxes.push_back(dcmShortSCUStorageSOPClassUIDs[i]); | |
147 } | |
148 } | |
149 | |
150 // Flatten the fallback transfer syntaxes array | |
151 const char* asPreferred[1] = { preferredTransferSyntax.c_str() }; | |
152 | |
153 fallbackSyntaxes.erase(preferredTransferSyntax); | 144 fallbackSyntaxes.erase(preferredTransferSyntax); |
154 | 145 |
146 // Flatten an array with the fallback transfer syntaxes | |
155 std::vector<const char*> asFallback; | 147 std::vector<const char*> asFallback; |
156 asFallback.reserve(fallbackSyntaxes.size()); | 148 asFallback.reserve(fallbackSyntaxes.size()); |
157 for (std::set<std::string>::const_iterator | 149 for (std::set<std::string>::const_iterator |
158 it = fallbackSyntaxes.begin(); it != fallbackSyntaxes.end(); ++it) | 150 it = fallbackSyntaxes.begin(); it != fallbackSyntaxes.end(); ++it) |
159 { | 151 { |
160 asFallback.push_back(it->c_str()); | 152 asFallback.push_back(it->c_str()); |
161 } | 153 } |
162 | 154 |
155 CheckStorageSOPClassesInvariant(); | |
163 unsigned int presentationContextId = 1; | 156 unsigned int presentationContextId = 1; |
164 for (size_t i = 0; i < transferSyntaxes.size(); i++) | 157 |
165 { | 158 for (std::list<std::string>::const_iterator it = reservedStorageSOPClasses_.begin(); |
166 Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, | 159 it != reservedStorageSOPClasses_.end(); it++) |
167 transferSyntaxes[i].c_str(), asPreferred, 1)); | 160 { |
168 presentationContextId += 2; | 161 RegisterStorageSOPClass(pimpl_->params_, presentationContextId, |
169 | 162 *it, asPreferred, asFallback); |
170 if (asFallback.size() > 0) | 163 } |
171 { | 164 |
172 Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, | 165 for (std::set<std::string>::const_iterator it = storageSOPClasses_.begin(); |
173 transferSyntaxes[i].c_str(), &asFallback[0], asFallback.size())); | 166 it != storageSOPClasses_.end(); it++) |
174 presentationContextId += 2; | 167 { |
175 } | 168 RegisterStorageSOPClass(pimpl_->params_, presentationContextId, |
169 *it, asPreferred, asFallback); | |
170 } | |
171 | |
172 for (std::set<std::string>::const_iterator it = defaultStorageSOPClasses_.begin(); | |
173 it != defaultStorageSOPClasses_.end(); it++) | |
174 { | |
175 RegisterStorageSOPClass(pimpl_->params_, presentationContextId, | |
176 *it, asPreferred, asFallback); | |
176 } | 177 } |
177 } | 178 } |
178 | 179 |
179 | 180 |
180 static bool IsGenericTransferSyntax(const std::string& syntax) | 181 static bool IsGenericTransferSyntax(const std::string& syntax) |
190 CheckIsOpen(); | 191 CheckIsOpen(); |
191 | 192 |
192 DcmFileFormat dcmff; | 193 DcmFileFormat dcmff; |
193 Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength)); | 194 Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength)); |
194 | 195 |
196 // Determine the storage SOP class UID for this instance | |
197 static const DcmTagKey DCM_SOP_CLASS_UID(0x0008, 0x0016); | |
198 OFString sopClassUid; | |
199 if (dcmff.getDataset()->findAndGetOFString(DCM_SOP_CLASS_UID, sopClassUid).good()) | |
200 { | |
201 connection.AddStorageSOPClass(sopClassUid.c_str()); | |
202 } | |
203 | |
195 // Determine whether a new presentation context must be | 204 // Determine whether a new presentation context must be |
196 // negociated, depending on the transfer syntax of this instance | 205 // negotiated, depending on the transfer syntax of this instance |
197 DcmXfer xfer(dcmff.getDataset()->getOriginalXfer()); | 206 DcmXfer xfer(dcmff.getDataset()->getOriginalXfer()); |
198 const std::string syntax(xfer.getXferID()); | 207 const std::string syntax(xfer.getXferID()); |
199 bool isGeneric = IsGenericTransferSyntax(syntax); | 208 bool isGeneric = IsGenericTransferSyntax(syntax); |
200 | 209 |
201 if (isGeneric ^ IsGenericTransferSyntax(connection.GetPreferredTransferSyntax())) | 210 if (isGeneric ^ IsGenericTransferSyntax(connection.GetPreferredTransferSyntax())) |
202 { | 211 { |
203 // Making a generic-to-specific or specific-to-generic change of | 212 // Making a generic-to-specific or specific-to-generic change of |
204 // the transfer syntax. Renegociate the connection. | 213 // the transfer syntax. Renegotiate the connection. |
205 LOG(INFO) << "Renegociating a C-Store association due to a change in the transfer syntax"; | 214 LOG(INFO) << "Change in the transfer syntax: the C-Store associated must be renegotiated"; |
206 | 215 |
207 if (isGeneric) | 216 if (isGeneric) |
208 { | 217 { |
209 connection.ResetPreferredTransferSyntax(); | 218 connection.ResetPreferredTransferSyntax(); |
210 } | 219 } |
211 else | 220 else |
212 { | 221 { |
213 connection.SetPreferredTransferSyntax(syntax); | 222 connection.SetPreferredTransferSyntax(syntax); |
214 } | 223 } |
215 | 224 } |
225 | |
226 if (!connection.IsOpen()) | |
227 { | |
228 LOG(INFO) << "Renegotiating a C-Store association due to a change in the parameters"; | |
216 connection.Open(); | 229 connection.Open(); |
217 } | 230 } |
218 | 231 |
219 // Figure out which SOP class and SOP instance is encapsulated in the file | 232 // Figure out which SOP class and SOP instance is encapsulated in the file |
220 DIC_UI sopClass; | 233 DIC_UI sopClass; |
230 { | 243 { |
231 const char *modalityName = dcmSOPClassUIDToModality(sopClass); | 244 const char *modalityName = dcmSOPClassUIDToModality(sopClass); |
232 if (!modalityName) modalityName = dcmFindNameOfUID(sopClass); | 245 if (!modalityName) modalityName = dcmFindNameOfUID(sopClass); |
233 if (!modalityName) modalityName = "unknown SOP class"; | 246 if (!modalityName) modalityName = "unknown SOP class"; |
234 throw OrthancException("DicomUserConnection: No presentation context for modality " + | 247 throw OrthancException("DicomUserConnection: No presentation context for modality " + |
235 std::string(modalityName)); | 248 std::string(modalityName)); |
236 } | 249 } |
237 | 250 |
238 // Prepare the transmission of data | 251 // Prepare the transmission of data |
239 T_DIMSE_C_StoreRQ req; | 252 T_DIMSE_C_StoreRQ req; |
240 memset(&req, 0, sizeof(req)); | 253 memset(&req, 0, sizeof(req)); |
286 | 299 |
287 const char* sopClass; | 300 const char* sopClass; |
288 std::auto_ptr<DcmDataset> dataset(ToDcmtkBridge::Convert(fields)); | 301 std::auto_ptr<DcmDataset> dataset(ToDcmtkBridge::Convert(fields)); |
289 switch (model) | 302 switch (model) |
290 { | 303 { |
291 case FindRootModel_Patient: | 304 case FindRootModel_Patient: |
292 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "PATIENT"); | 305 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "PATIENT"); |
293 sopClass = UID_FINDPatientRootQueryRetrieveInformationModel; | 306 sopClass = UID_FINDPatientRootQueryRetrieveInformationModel; |
294 | 307 |
295 // Accession number | 308 // Accession number |
296 if (!fields.HasTag(0x0008, 0x0050)) | 309 if (!fields.HasTag(0x0008, 0x0050)) |
297 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); | 310 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); |
298 | 311 |
299 // Patient ID | 312 // Patient ID |
300 if (!fields.HasTag(0x0010, 0x0020)) | 313 if (!fields.HasTag(0x0010, 0x0020)) |
301 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0010, 0x0020), ""); | 314 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0010, 0x0020), ""); |
302 | 315 |
303 break; | 316 break; |
304 | 317 |
305 case FindRootModel_Study: | 318 case FindRootModel_Study: |
306 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "STUDY"); | 319 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "STUDY"); |
307 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; | 320 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; |
308 | 321 |
309 // Accession number | 322 // Accession number |
310 if (!fields.HasTag(0x0008, 0x0050)) | 323 if (!fields.HasTag(0x0008, 0x0050)) |
311 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); | 324 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); |
312 | 325 |
313 // Study instance UID | 326 // Study instance UID |
314 if (!fields.HasTag(0x0020, 0x000d)) | 327 if (!fields.HasTag(0x0020, 0x000d)) |
315 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); | 328 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); |
316 | 329 |
317 break; | 330 break; |
318 | 331 |
319 case FindRootModel_Series: | 332 case FindRootModel_Series: |
320 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "SERIES"); | 333 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "SERIES"); |
321 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; | 334 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; |
322 | 335 |
323 // Accession number | 336 // Accession number |
324 if (!fields.HasTag(0x0008, 0x0050)) | 337 if (!fields.HasTag(0x0008, 0x0050)) |
325 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); | 338 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); |
326 | 339 |
327 // Study instance UID | 340 // Study instance UID |
328 if (!fields.HasTag(0x0020, 0x000d)) | 341 if (!fields.HasTag(0x0020, 0x000d)) |
329 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); | 342 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); |
330 | 343 |
331 // Series instance UID | 344 // Series instance UID |
332 if (!fields.HasTag(0x0020, 0x000e)) | 345 if (!fields.HasTag(0x0020, 0x000e)) |
333 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), ""); | 346 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), ""); |
334 | 347 |
335 break; | 348 break; |
336 | 349 |
337 case FindRootModel_Instance: | 350 case FindRootModel_Instance: |
338 if (manufacturer_ == ModalityManufacturer_ClearCanvas || | 351 if (manufacturer_ == ModalityManufacturer_ClearCanvas || |
339 manufacturer_ == ModalityManufacturer_Dcm4Chee) | 352 manufacturer_ == ModalityManufacturer_Dcm4Chee) |
340 { | 353 { |
341 // This is a particular case for ClearCanvas, thanks to Peter Somlo <peter.somlo@gmail.com>. | 354 // This is a particular case for ClearCanvas, thanks to Peter Somlo <peter.somlo@gmail.com>. |
342 // https://groups.google.com/d/msg/orthanc-users/j-6C3MAVwiw/iolB9hclom8J | 355 // https://groups.google.com/d/msg/orthanc-users/j-6C3MAVwiw/iolB9hclom8J |
343 // http://www.clearcanvas.ca/Home/Community/OldForums/tabid/526/aff/11/aft/14670/afv/topic/Default.aspx | 356 // http://www.clearcanvas.ca/Home/Community/OldForums/tabid/526/aff/11/aft/14670/afv/topic/Default.aspx |
344 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "IMAGE"); | 357 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "IMAGE"); |
345 } | 358 } |
346 else | 359 else |
347 { | 360 { |
348 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "INSTANCE"); | 361 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "INSTANCE"); |
349 } | 362 } |
350 | 363 |
351 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; | 364 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; |
352 | 365 |
353 // Accession number | 366 // Accession number |
354 if (!fields.HasTag(0x0008, 0x0050)) | 367 if (!fields.HasTag(0x0008, 0x0050)) |
355 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); | 368 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); |
356 | 369 |
357 // Study instance UID | 370 // Study instance UID |
358 if (!fields.HasTag(0x0020, 0x000d)) | 371 if (!fields.HasTag(0x0020, 0x000d)) |
359 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); | 372 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); |
360 | 373 |
361 // Series instance UID | 374 // Series instance UID |
362 if (!fields.HasTag(0x0020, 0x000e)) | 375 if (!fields.HasTag(0x0020, 0x000e)) |
363 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), ""); | 376 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), ""); |
364 | 377 |
365 // SOP Instance UID | 378 // SOP Instance UID |
366 if (!fields.HasTag(0x0008, 0x0018)) | 379 if (!fields.HasTag(0x0008, 0x0018)) |
367 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0018), ""); | 380 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0018), ""); |
368 | 381 |
369 break; | 382 break; |
370 | 383 |
371 default: | 384 default: |
372 throw OrthancException(ErrorCode_ParameterOutOfRange); | 385 throw OrthancException(ErrorCode_ParameterOutOfRange); |
373 } | 386 } |
374 | 387 |
375 // Figure out which of the accepted presentation contexts should be used | 388 // Figure out which of the accepted presentation contexts should be used |
376 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); | 389 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); |
377 if (presID == 0) | 390 if (presID == 0) |
499 | 512 |
500 Check(cond); | 513 Check(cond); |
501 } | 514 } |
502 | 515 |
503 | 516 |
517 void DicomUserConnection::ResetStorageSOPClasses() | |
518 { | |
519 CheckStorageSOPClassesInvariant(); | |
520 | |
521 storageSOPClasses_.clear(); | |
522 defaultStorageSOPClasses_.clear(); | |
523 | |
524 // Copy the short list of storage SOP classes from DCMTK, making | |
525 // room for the 4 SOP classes reserved for C-ECHO, C-FIND, C-MOVE. | |
526 | |
527 std::set<std::string> uncommon; | |
528 uncommon.insert(UID_BlendingSoftcopyPresentationStateStorage); | |
529 uncommon.insert(UID_GrayscaleSoftcopyPresentationStateStorage); | |
530 uncommon.insert(UID_ColorSoftcopyPresentationStateStorage); | |
531 uncommon.insert(UID_PseudoColorSoftcopyPresentationStateStorage); | |
532 | |
533 // Add the storage syntaxes for C-STORE | |
534 for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs - 1; i++) | |
535 { | |
536 if (uncommon.find(dcmShortSCUStorageSOPClassUIDs[i]) == uncommon.end()) | |
537 { | |
538 defaultStorageSOPClasses_.insert(dcmShortSCUStorageSOPClassUIDs[i]); | |
539 } | |
540 } | |
541 | |
542 CheckStorageSOPClassesInvariant(); | |
543 } | |
544 | |
545 | |
504 DicomUserConnection::DicomUserConnection() : | 546 DicomUserConnection::DicomUserConnection() : |
505 pimpl_(new PImpl), | 547 pimpl_(new PImpl), |
506 preferredTransferSyntax_(DEFAULT_PREFERRED_TRANSFER_SYNTAX), | 548 preferredTransferSyntax_(DEFAULT_PREFERRED_TRANSFER_SYNTAX), |
507 localAet_("STORESCU"), | 549 localAet_("STORESCU"), |
508 distantAet_("ANY-SCP"), | 550 distantAet_("ANY-SCP"), |
512 manufacturer_ = ModalityManufacturer_Generic; | 554 manufacturer_ = ModalityManufacturer_Generic; |
513 | 555 |
514 pimpl_->net_ = NULL; | 556 pimpl_->net_ = NULL; |
515 pimpl_->params_ = NULL; | 557 pimpl_->params_ = NULL; |
516 pimpl_->assoc_ = NULL; | 558 pimpl_->assoc_ = NULL; |
559 | |
560 // SOP classes for C-ECHO, C-FIND and C-MOVE | |
561 reservedStorageSOPClasses_.push_back(UID_VerificationSOPClass); | |
562 reservedStorageSOPClasses_.push_back(UID_FINDPatientRootQueryRetrieveInformationModel); | |
563 reservedStorageSOPClasses_.push_back(UID_FINDStudyRootQueryRetrieveInformationModel); | |
564 reservedStorageSOPClasses_.push_back(UID_MOVEStudyRootQueryRetrieveInformationModel); | |
565 | |
566 ResetStorageSOPClasses(); | |
517 } | 567 } |
518 | 568 |
519 DicomUserConnection::~DicomUserConnection() | 569 DicomUserConnection::~DicomUserConnection() |
520 { | 570 { |
521 Close(); | 571 Close(); |
605 gethostname(localHost, HOST_NAME_MAX - 1); | 655 gethostname(localHost, HOST_NAME_MAX - 1); |
606 | 656 |
607 char distantHostAndPort[HOST_NAME_MAX]; | 657 char distantHostAndPort[HOST_NAME_MAX]; |
608 | 658 |
609 #ifdef _MSC_VER | 659 #ifdef _MSC_VER |
610 _snprintf | 660 _snprintf |
611 #else | 661 #else |
612 snprintf | 662 snprintf |
613 #endif | 663 #endif |
614 (distantHostAndPort, HOST_NAME_MAX - 1, "%s:%d", distantHost_.c_str(), distantPort_); | 664 (distantHostAndPort, HOST_NAME_MAX - 1, "%s:%d", distantHost_.c_str(), distantPort_); |
615 | 665 |
616 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, distantHostAndPort)); | 666 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, distantHostAndPort)); |
617 | 667 |
618 // Set various options | 668 // Set various options |
619 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false)); | 669 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false)); |
740 void DicomUserConnection::SetConnectionTimeout(uint32_t seconds) | 790 void DicomUserConnection::SetConnectionTimeout(uint32_t seconds) |
741 { | 791 { |
742 dcmConnectionTimeout.set(seconds); | 792 dcmConnectionTimeout.set(seconds); |
743 } | 793 } |
744 | 794 |
795 | |
796 void DicomUserConnection::CheckStorageSOPClassesInvariant() const | |
797 { | |
798 assert(storageSOPClasses_.size() + | |
799 defaultStorageSOPClasses_.size() + | |
800 reservedStorageSOPClasses_.size() <= MAXIMUM_STORAGE_SOP_CLASSES); | |
801 } | |
802 | |
803 void DicomUserConnection::AddStorageSOPClass(const char* sop) | |
804 { | |
805 CheckStorageSOPClassesInvariant(); | |
806 | |
807 if (storageSOPClasses_.find(sop) != storageSOPClasses_.end()) | |
808 { | |
809 // This storage SOP class is already explicitly registered. Do | |
810 // nothing. | |
811 return; | |
812 } | |
813 | |
814 if (defaultStorageSOPClasses_.find(sop) != defaultStorageSOPClasses_.end()) | |
815 { | |
816 // This storage SOP class is not explicitly registered, but is | |
817 // used by default. Just register it explicitly. | |
818 defaultStorageSOPClasses_.erase(sop); | |
819 storageSOPClasses_.insert(sop); | |
820 | |
821 CheckStorageSOPClassesInvariant(); | |
822 return; | |
823 } | |
824 | |
825 // This storage SOP class is neither explicitly, nor implicitly | |
826 // registered. Close the connection and register it explicitly. | |
827 | |
828 Close(); | |
829 | |
830 if (reservedStorageSOPClasses_.size() + | |
831 storageSOPClasses_.size() >= MAXIMUM_STORAGE_SOP_CLASSES) // (*) | |
832 { | |
833 // The maximum number of SOP classes is reached | |
834 ResetStorageSOPClasses(); | |
835 defaultStorageSOPClasses_.erase(sop); | |
836 } | |
837 else if (reservedStorageSOPClasses_.size() + storageSOPClasses_.size() + | |
838 defaultStorageSOPClasses_.size() >= MAXIMUM_STORAGE_SOP_CLASSES) | |
839 { | |
840 // Make room in the default storage syntaxes | |
841 assert(defaultStorageSOPClasses_.size() > 0); // Necessarily true because condition (*) is false | |
842 defaultStorageSOPClasses_.erase(*defaultStorageSOPClasses_.rbegin()); | |
843 } | |
844 | |
845 // Explicitly register the new storage syntax | |
846 storageSOPClasses_.insert(sop); | |
847 | |
848 CheckStorageSOPClassesInvariant(); | |
849 } | |
850 | |
745 } | 851 } |