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();