Mercurial > hg > orthanc
comparison OrthancServer/DicomProtocol/DicomUserConnection.cpp @ 662:70161eb45b5c
orthanc can act as a C-Store SCU for JPEG transfer syntax
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 06 Nov 2013 16:19:25 +0100 |
parents | 5425bb6f1ea5 |
children | b8383ac0b227 |
comparison
equal
deleted
inserted
replaced
661:d233b5090105 | 662:70161eb45b5c |
---|---|
37 #include "../FromDcmtkBridge.h" | 37 #include "../FromDcmtkBridge.h" |
38 | 38 |
39 #include <dcmtk/dcmdata/dcistrmb.h> | 39 #include <dcmtk/dcmdata/dcistrmb.h> |
40 #include <dcmtk/dcmdata/dcistrmf.h> | 40 #include <dcmtk/dcmdata/dcistrmf.h> |
41 #include <dcmtk/dcmdata/dcfilefo.h> | 41 #include <dcmtk/dcmdata/dcfilefo.h> |
42 #include <dcmtk/dcmdata/dcmetinf.h> | |
42 #include <dcmtk/dcmnet/diutil.h> | 43 #include <dcmtk/dcmnet/diutil.h> |
43 | 44 |
44 #include <set> | 45 #include <set> |
46 #include <glog/logging.h> | |
45 | 47 |
46 | 48 |
47 | 49 |
48 #ifdef _WIN32 | 50 #ifdef _WIN32 |
49 /** | 51 /** |
54 **/ | 56 **/ |
55 #define HOST_NAME_MAX 256 | 57 #define HOST_NAME_MAX 256 |
56 #endif | 58 #endif |
57 | 59 |
58 | 60 |
61 static const char* DEFAULT_PREFERRED_TRANSFER_SYNTAX = UID_LittleEndianImplicitTransferSyntax; | |
62 | |
59 namespace Orthanc | 63 namespace Orthanc |
60 { | 64 { |
61 struct DicomUserConnection::PImpl | 65 struct DicomUserConnection::PImpl |
62 { | 66 { |
63 // Connection state | 67 // Connection state |
70 return assoc_ != NULL; | 74 return assoc_ != NULL; |
71 } | 75 } |
72 | 76 |
73 void CheckIsOpen() const; | 77 void CheckIsOpen() const; |
74 | 78 |
75 void Store(DcmInputStream& is); | 79 void Store(DcmInputStream& is, DicomUserConnection& connection); |
76 }; | 80 }; |
77 | 81 |
78 | 82 |
79 static void Check(const OFCondition& cond) | 83 static void Check(const OFCondition& cond) |
80 { | 84 { |
104 Close(); | 108 Close(); |
105 localAet_ = other.localAet_; | 109 localAet_ = other.localAet_; |
106 distantAet_ = other.distantAet_; | 110 distantAet_ = other.distantAet_; |
107 distantHost_ = other.distantHost_; | 111 distantHost_ = other.distantHost_; |
108 distantPort_ = other.distantPort_; | 112 distantPort_ = other.distantPort_; |
109 } | 113 manufacturer_ = other.manufacturer_; |
110 | 114 preferredTransferSyntax_ = other.preferredTransferSyntax_; |
111 | 115 } |
112 void DicomUserConnection::SetupPresentationContexts() | 116 |
113 { | 117 |
114 // The preferred abstract syntax | 118 void DicomUserConnection::SetupPresentationContexts(const std::string& preferredTransferSyntax) |
115 std::string preferredSyntax = UID_LittleEndianImplicitTransferSyntax; | 119 { |
116 | 120 // Fallback transfer syntaxes |
117 // Fallback abstract syntaxes | 121 std::set<std::string> fallbackSyntaxes; |
118 std::set<std::string> abstractSyntaxes; | 122 fallbackSyntaxes.insert(UID_LittleEndianExplicitTransferSyntax); |
119 abstractSyntaxes.insert(UID_LittleEndianExplicitTransferSyntax); | 123 fallbackSyntaxes.insert(UID_BigEndianExplicitTransferSyntax); |
120 abstractSyntaxes.insert(UID_BigEndianExplicitTransferSyntax); | 124 fallbackSyntaxes.insert(UID_LittleEndianImplicitTransferSyntax); |
121 abstractSyntaxes.insert(UID_LittleEndianImplicitTransferSyntax); | |
122 abstractSyntaxes.erase(preferredSyntax); | |
123 assert(abstractSyntaxes.size() == 2); | |
124 | 125 |
125 // Transfer syntaxes for C-ECHO, C-FIND and C-MOVE | 126 // Transfer syntaxes for C-ECHO, C-FIND and C-MOVE |
126 std::vector<std::string> transferSyntaxes; | 127 std::vector<std::string> transferSyntaxes; |
127 transferSyntaxes.push_back(UID_VerificationSOPClass); | 128 transferSyntaxes.push_back(UID_VerificationSOPClass); |
128 transferSyntaxes.push_back(UID_FINDPatientRootQueryRetrieveInformationModel); | 129 transferSyntaxes.push_back(UID_FINDPatientRootQueryRetrieveInformationModel); |
144 { | 145 { |
145 transferSyntaxes.push_back(dcmShortSCUStorageSOPClassUIDs[i]); | 146 transferSyntaxes.push_back(dcmShortSCUStorageSOPClassUIDs[i]); |
146 } | 147 } |
147 } | 148 } |
148 | 149 |
149 // Flatten the fallback abstract syntaxes array | 150 // Flatten the fallback transfer syntaxes array |
150 const char* asPreferred[1] = { preferredSyntax.c_str() }; | 151 const char* asPreferred[1] = { preferredTransferSyntax.c_str() }; |
151 const char* asFallback[2]; | 152 |
152 std::set<std::string>::const_iterator it = abstractSyntaxes.begin(); | 153 fallbackSyntaxes.erase(preferredTransferSyntax); |
153 asFallback[0] = it->c_str(); | 154 |
154 ++it; | 155 std::vector<const char*> asFallback; |
155 asFallback[1] = it->c_str(); | 156 asFallback.reserve(fallbackSyntaxes.size()); |
157 for (std::set<std::string>::const_iterator | |
158 it = fallbackSyntaxes.begin(); it != fallbackSyntaxes.end(); ++it) | |
159 { | |
160 asFallback.push_back(it->c_str()); | |
161 } | |
156 | 162 |
157 unsigned int presentationContextId = 1; | 163 unsigned int presentationContextId = 1; |
158 for (size_t i = 0; i < transferSyntaxes.size(); i++) | 164 for (size_t i = 0; i < transferSyntaxes.size(); i++) |
159 { | 165 { |
160 Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, | 166 Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, |
161 transferSyntaxes[i].c_str(), asPreferred, 1)); | 167 transferSyntaxes[i].c_str(), asPreferred, 1)); |
162 presentationContextId += 2; | 168 presentationContextId += 2; |
163 | 169 |
164 Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, | 170 if (asFallback.size() > 0) |
165 transferSyntaxes[i].c_str(), asFallback, 2)); | 171 { |
166 presentationContextId += 2; | 172 Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, |
167 } | 173 transferSyntaxes[i].c_str(), &asFallback[0], asFallback.size())); |
168 } | 174 presentationContextId += 2; |
169 | 175 } |
170 | 176 } |
171 void DicomUserConnection::PImpl::Store(DcmInputStream& is) | 177 } |
178 | |
179 | |
180 static bool IsGenericTransferSyntax(const std::string& syntax) | |
181 { | |
182 return (syntax == UID_LittleEndianExplicitTransferSyntax || | |
183 syntax == UID_BigEndianExplicitTransferSyntax || | |
184 syntax == UID_LittleEndianImplicitTransferSyntax); | |
185 } | |
186 | |
187 | |
188 void DicomUserConnection::PImpl::Store(DcmInputStream& is, DicomUserConnection& connection) | |
172 { | 189 { |
173 CheckIsOpen(); | 190 CheckIsOpen(); |
174 | 191 |
175 DcmFileFormat dcmff; | 192 DcmFileFormat dcmff; |
176 Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength)); | 193 Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength)); |
194 | |
195 // Determine whether a new presentation context must be | |
196 // negociated, depending on the transfer syntax of this instance | |
197 DcmXfer xfer(dcmff.getDataset()->getOriginalXfer()); | |
198 const std::string syntax(xfer.getXferID()); | |
199 bool isGeneric = IsGenericTransferSyntax(syntax); | |
200 | |
201 if (isGeneric ^ IsGenericTransferSyntax(connection.GetPreferredTransferSyntax())) | |
202 { | |
203 // Making a generic-to-specific or specific-to-generic change of | |
204 // the transfer syntax. Renegociate the connection. | |
205 LOG(INFO) << "Renegociating a C-Store association due to a change in the transfer syntax"; | |
206 | |
207 if (isGeneric) | |
208 { | |
209 connection.ResetPreferredTransferSyntax(); | |
210 } | |
211 else | |
212 { | |
213 connection.SetPreferredTransferSyntax(syntax); | |
214 } | |
215 | |
216 connection.Open(); | |
217 } | |
177 | 218 |
178 // Figure out which SOP class and SOP instance is encapsulated in the file | 219 // Figure out which SOP class and SOP instance is encapsulated in the file |
179 DIC_UI sopClass; | 220 DIC_UI sopClass; |
180 DIC_UI sopInstance; | 221 DIC_UI sopInstance; |
181 if (!DU_findSOPClassAndInstanceInDataSet(dcmff.getDataset(), sopClass, sopInstance)) | 222 if (!DU_findSOPClassAndInstanceInDataSet(dcmff.getDataset(), sopClass, sopInstance)) |
466 distantAet_("ANY-SCP"), | 507 distantAet_("ANY-SCP"), |
467 distantHost_("127.0.0.1") | 508 distantHost_("127.0.0.1") |
468 { | 509 { |
469 distantPort_ = 104; | 510 distantPort_ = 104; |
470 manufacturer_ = ModalityManufacturer_Generic; | 511 manufacturer_ = ModalityManufacturer_Generic; |
512 preferredTransferSyntax_ = DEFAULT_PREFERRED_TRANSFER_SYNTAX; | |
471 | 513 |
472 pimpl_->net_ = NULL; | 514 pimpl_->net_ = NULL; |
473 pimpl_->params_ = NULL; | 515 pimpl_->params_ = NULL; |
474 pimpl_->assoc_ = NULL; | 516 pimpl_->assoc_ = NULL; |
475 } | 517 } |
479 Close(); | 521 Close(); |
480 } | 522 } |
481 | 523 |
482 void DicomUserConnection::SetLocalApplicationEntityTitle(const std::string& aet) | 524 void DicomUserConnection::SetLocalApplicationEntityTitle(const std::string& aet) |
483 { | 525 { |
484 Close(); | 526 if (localAet_ != aet) |
485 localAet_ = aet; | 527 { |
528 Close(); | |
529 localAet_ = aet; | |
530 } | |
486 } | 531 } |
487 | 532 |
488 void DicomUserConnection::SetDistantApplicationEntityTitle(const std::string& aet) | 533 void DicomUserConnection::SetDistantApplicationEntityTitle(const std::string& aet) |
489 { | 534 { |
490 Close(); | 535 if (distantAet_ != aet) |
491 distantAet_ = aet; | 536 { |
537 Close(); | |
538 distantAet_ = aet; | |
539 } | |
492 } | 540 } |
493 | 541 |
494 void DicomUserConnection::SetDistantManufacturer(ModalityManufacturer manufacturer) | 542 void DicomUserConnection::SetDistantManufacturer(ModalityManufacturer manufacturer) |
495 { | 543 { |
496 Close(); | 544 if (manufacturer_ != manufacturer) |
497 manufacturer_ = manufacturer; | 545 { |
546 Close(); | |
547 manufacturer_ = manufacturer; | |
548 } | |
549 } | |
550 | |
551 void DicomUserConnection::ResetPreferredTransferSyntax() | |
552 { | |
553 SetPreferredTransferSyntax(DEFAULT_PREFERRED_TRANSFER_SYNTAX); | |
554 } | |
555 | |
556 void DicomUserConnection::SetPreferredTransferSyntax(const std::string& preferredTransferSyntax) | |
557 { | |
558 if (preferredTransferSyntax_ != preferredTransferSyntax) | |
559 { | |
560 Close(); | |
561 preferredTransferSyntax_ = preferredTransferSyntax; | |
562 } | |
498 } | 563 } |
499 | 564 |
500 | 565 |
501 void DicomUserConnection::SetDistantHost(const std::string& host) | 566 void DicomUserConnection::SetDistantHost(const std::string& host) |
502 { | 567 { |
503 if (host.size() > HOST_NAME_MAX - 10) | 568 if (distantHost_ != host) |
504 { | 569 { |
505 throw OrthancException("Distant host name is too long"); | 570 if (host.size() > HOST_NAME_MAX - 10) |
506 } | 571 { |
507 | 572 throw OrthancException("Distant host name is too long"); |
508 Close(); | 573 } |
509 distantHost_ = host; | 574 |
575 Close(); | |
576 distantHost_ = host; | |
577 } | |
510 } | 578 } |
511 | 579 |
512 void DicomUserConnection::SetDistantPort(uint16_t port) | 580 void DicomUserConnection::SetDistantPort(uint16_t port) |
513 { | 581 { |
514 Close(); | 582 if (distantPort_ != port) |
515 distantPort_ = port; | 583 { |
584 Close(); | |
585 distantPort_ = port; | |
586 } | |
516 } | 587 } |
517 | 588 |
518 void DicomUserConnection::Open() | 589 void DicomUserConnection::Open() |
519 { | 590 { |
520 Close(); | 591 if (IsOpen()) |
592 { | |
593 // Don't reopen the connection | |
594 return; | |
595 } | |
521 | 596 |
522 Check(ASC_initializeNetwork(NET_REQUESTOR, 0, /*opt_acse_timeout*/ 30, &pimpl_->net_)); | 597 Check(ASC_initializeNetwork(NET_REQUESTOR, 0, /*opt_acse_timeout*/ 30, &pimpl_->net_)); |
523 Check(ASC_createAssociationParameters(&pimpl_->params_, /*opt_maxReceivePDULength*/ ASC_DEFAULTMAXPDU)); | 598 Check(ASC_createAssociationParameters(&pimpl_->params_, /*opt_maxReceivePDULength*/ ASC_DEFAULTMAXPDU)); |
524 | 599 |
525 // Set this application's title and the called application's title in the params | 600 // Set this application's title and the called application's title in the params |
541 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, distantHostAndPort)); | 616 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, distantHostAndPort)); |
542 | 617 |
543 // Set various options | 618 // Set various options |
544 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false)); | 619 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false)); |
545 | 620 |
546 SetupPresentationContexts(); | 621 SetupPresentationContexts(preferredTransferSyntax_); |
547 | 622 |
548 // Do the association | 623 // Do the association |
549 Check(ASC_requestAssociation(pimpl_->net_, pimpl_->params_, &pimpl_->assoc_)); | 624 Check(ASC_requestAssociation(pimpl_->net_, pimpl_->params_, &pimpl_->assoc_)); |
550 | 625 |
551 if (ASC_countAcceptedPresentationContexts(pimpl_->params_) == 0) | 626 if (ASC_countAcceptedPresentationContexts(pimpl_->params_) == 0) |
590 DcmInputBufferStream is; | 665 DcmInputBufferStream is; |
591 if (size > 0) | 666 if (size > 0) |
592 is.setBuffer(buffer, size); | 667 is.setBuffer(buffer, size); |
593 is.setEos(); | 668 is.setEos(); |
594 | 669 |
595 pimpl_->Store(is); | 670 pimpl_->Store(is, *this); |
596 } | 671 } |
597 | 672 |
598 void DicomUserConnection::Store(const std::string& buffer) | 673 void DicomUserConnection::Store(const std::string& buffer) |
599 { | 674 { |
600 if (buffer.size() > 0) | 675 if (buffer.size() > 0) |
605 | 680 |
606 void DicomUserConnection::StoreFile(const std::string& path) | 681 void DicomUserConnection::StoreFile(const std::string& path) |
607 { | 682 { |
608 // Prepare an input stream for the file | 683 // Prepare an input stream for the file |
609 DcmInputFileStream is(path.c_str()); | 684 DcmInputFileStream is(path.c_str()); |
610 pimpl_->Store(is); | 685 pimpl_->Store(is, *this); |
611 } | 686 } |
612 | 687 |
613 bool DicomUserConnection::Echo() | 688 bool DicomUserConnection::Echo() |
614 { | 689 { |
615 CheckIsOpen(); | 690 CheckIsOpen(); |