# HG changeset patch # User Sebastien Jodogne # Date 1383751165 -3600 # Node ID 70161eb45b5c1ca7aeac89259c5a716690cc7818 # Parent d233b50901055502f133416b4ae67abbc05fd766 orthanc can act as a C-Store SCU for JPEG transfer syntax diff -r d233b5090105 -r 70161eb45b5c NEWS --- a/NEWS Tue Nov 05 17:41:25 2013 +0100 +++ b/NEWS Wed Nov 06 16:19:25 2013 +0100 @@ -2,7 +2,7 @@ =============================== -* Accept more transfer syntaxes for C-Store SCP (including JPEG) +* Accept more transfer syntaxes for C-Store SCP and SCU (notably JPEG) * Create the meta-header when receiving files through C-Store SCP * Fixes and improvements thanks to the static analyzer cppcheck diff -r d233b5090105 -r 70161eb45b5c OrthancServer/DicomProtocol/DicomUserConnection.cpp --- a/OrthancServer/DicomProtocol/DicomUserConnection.cpp Tue Nov 05 17:41:25 2013 +0100 +++ b/OrthancServer/DicomProtocol/DicomUserConnection.cpp Wed Nov 06 16:19:25 2013 +0100 @@ -39,9 +39,11 @@ #include #include #include +#include #include #include +#include @@ -56,6 +58,8 @@ #endif +static const char* DEFAULT_PREFERRED_TRANSFER_SYNTAX = UID_LittleEndianImplicitTransferSyntax; + namespace Orthanc { struct DicomUserConnection::PImpl @@ -72,7 +76,7 @@ void CheckIsOpen() const; - void Store(DcmInputStream& is); + void Store(DcmInputStream& is, DicomUserConnection& connection); }; @@ -106,21 +110,18 @@ distantAet_ = other.distantAet_; distantHost_ = other.distantHost_; distantPort_ = other.distantPort_; + manufacturer_ = other.manufacturer_; + preferredTransferSyntax_ = other.preferredTransferSyntax_; } - void DicomUserConnection::SetupPresentationContexts() + void DicomUserConnection::SetupPresentationContexts(const std::string& preferredTransferSyntax) { - // The preferred abstract syntax - std::string preferredSyntax = UID_LittleEndianImplicitTransferSyntax; - - // Fallback abstract syntaxes - std::set abstractSyntaxes; - abstractSyntaxes.insert(UID_LittleEndianExplicitTransferSyntax); - abstractSyntaxes.insert(UID_BigEndianExplicitTransferSyntax); - abstractSyntaxes.insert(UID_LittleEndianImplicitTransferSyntax); - abstractSyntaxes.erase(preferredSyntax); - assert(abstractSyntaxes.size() == 2); + // Fallback transfer syntaxes + std::set fallbackSyntaxes; + fallbackSyntaxes.insert(UID_LittleEndianExplicitTransferSyntax); + fallbackSyntaxes.insert(UID_BigEndianExplicitTransferSyntax); + fallbackSyntaxes.insert(UID_LittleEndianImplicitTransferSyntax); // Transfer syntaxes for C-ECHO, C-FIND and C-MOVE std::vector transferSyntaxes; @@ -146,13 +147,18 @@ } } - // Flatten the fallback abstract syntaxes array - const char* asPreferred[1] = { preferredSyntax.c_str() }; - const char* asFallback[2]; - std::set::const_iterator it = abstractSyntaxes.begin(); - asFallback[0] = it->c_str(); - ++it; - asFallback[1] = it->c_str(); + // Flatten the fallback transfer syntaxes array + const char* asPreferred[1] = { preferredTransferSyntax.c_str() }; + + fallbackSyntaxes.erase(preferredTransferSyntax); + + std::vector asFallback; + asFallback.reserve(fallbackSyntaxes.size()); + for (std::set::const_iterator + it = fallbackSyntaxes.begin(); it != fallbackSyntaxes.end(); ++it) + { + asFallback.push_back(it->c_str()); + } unsigned int presentationContextId = 1; for (size_t i = 0; i < transferSyntaxes.size(); i++) @@ -161,20 +167,55 @@ transferSyntaxes[i].c_str(), asPreferred, 1)); presentationContextId += 2; - Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, - transferSyntaxes[i].c_str(), asFallback, 2)); - presentationContextId += 2; + if (asFallback.size() > 0) + { + Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, + transferSyntaxes[i].c_str(), &asFallback[0], asFallback.size())); + presentationContextId += 2; + } } } - void DicomUserConnection::PImpl::Store(DcmInputStream& is) + static bool IsGenericTransferSyntax(const std::string& syntax) + { + return (syntax == UID_LittleEndianExplicitTransferSyntax || + syntax == UID_BigEndianExplicitTransferSyntax || + syntax == UID_LittleEndianImplicitTransferSyntax); + } + + + void DicomUserConnection::PImpl::Store(DcmInputStream& is, DicomUserConnection& connection) { CheckIsOpen(); DcmFileFormat dcmff; Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength)); + // Determine whether a new presentation context must be + // negociated, depending on the transfer syntax of this instance + DcmXfer xfer(dcmff.getDataset()->getOriginalXfer()); + const std::string syntax(xfer.getXferID()); + bool isGeneric = IsGenericTransferSyntax(syntax); + + if (isGeneric ^ IsGenericTransferSyntax(connection.GetPreferredTransferSyntax())) + { + // Making a generic-to-specific or specific-to-generic change of + // the transfer syntax. Renegociate the connection. + LOG(INFO) << "Renegociating a C-Store association due to a change in the transfer syntax"; + + if (isGeneric) + { + connection.ResetPreferredTransferSyntax(); + } + else + { + connection.SetPreferredTransferSyntax(syntax); + } + + connection.Open(); + } + // Figure out which SOP class and SOP instance is encapsulated in the file DIC_UI sopClass; DIC_UI sopInstance; @@ -468,6 +509,7 @@ { distantPort_ = 104; manufacturer_ = ModalityManufacturer_Generic; + preferredTransferSyntax_ = DEFAULT_PREFERRED_TRANSFER_SYNTAX; pimpl_->net_ = NULL; pimpl_->params_ = NULL; @@ -481,43 +523,76 @@ void DicomUserConnection::SetLocalApplicationEntityTitle(const std::string& aet) { - Close(); - localAet_ = aet; + if (localAet_ != aet) + { + Close(); + localAet_ = aet; + } } void DicomUserConnection::SetDistantApplicationEntityTitle(const std::string& aet) { - Close(); - distantAet_ = aet; + if (distantAet_ != aet) + { + Close(); + distantAet_ = aet; + } } void DicomUserConnection::SetDistantManufacturer(ModalityManufacturer manufacturer) { - Close(); - manufacturer_ = manufacturer; + if (manufacturer_ != manufacturer) + { + Close(); + manufacturer_ = manufacturer; + } + } + + void DicomUserConnection::ResetPreferredTransferSyntax() + { + SetPreferredTransferSyntax(DEFAULT_PREFERRED_TRANSFER_SYNTAX); + } + + void DicomUserConnection::SetPreferredTransferSyntax(const std::string& preferredTransferSyntax) + { + if (preferredTransferSyntax_ != preferredTransferSyntax) + { + Close(); + preferredTransferSyntax_ = preferredTransferSyntax; + } } void DicomUserConnection::SetDistantHost(const std::string& host) { - if (host.size() > HOST_NAME_MAX - 10) + if (distantHost_ != host) { - throw OrthancException("Distant host name is too long"); + if (host.size() > HOST_NAME_MAX - 10) + { + throw OrthancException("Distant host name is too long"); + } + + Close(); + distantHost_ = host; } - - Close(); - distantHost_ = host; } void DicomUserConnection::SetDistantPort(uint16_t port) { - Close(); - distantPort_ = port; + if (distantPort_ != port) + { + Close(); + distantPort_ = port; + } } void DicomUserConnection::Open() { - Close(); + if (IsOpen()) + { + // Don't reopen the connection + return; + } Check(ASC_initializeNetwork(NET_REQUESTOR, 0, /*opt_acse_timeout*/ 30, &pimpl_->net_)); Check(ASC_createAssociationParameters(&pimpl_->params_, /*opt_maxReceivePDULength*/ ASC_DEFAULTMAXPDU)); @@ -543,7 +618,7 @@ // Set various options Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false)); - SetupPresentationContexts(); + SetupPresentationContexts(preferredTransferSyntax_); // Do the association Check(ASC_requestAssociation(pimpl_->net_, pimpl_->params_, &pimpl_->assoc_)); @@ -592,7 +667,7 @@ is.setBuffer(buffer, size); is.setEos(); - pimpl_->Store(is); + pimpl_->Store(is, *this); } void DicomUserConnection::Store(const std::string& buffer) @@ -607,7 +682,7 @@ { // Prepare an input stream for the file DcmInputFileStream is(path.c_str()); - pimpl_->Store(is); + pimpl_->Store(is, *this); } bool DicomUserConnection::Echo() diff -r d233b5090105 -r 70161eb45b5c OrthancServer/DicomProtocol/DicomUserConnection.h --- a/OrthancServer/DicomProtocol/DicomUserConnection.h Tue Nov 05 17:41:25 2013 +0100 +++ b/OrthancServer/DicomProtocol/DicomUserConnection.h Wed Nov 06 16:19:25 2013 +0100 @@ -56,6 +56,7 @@ boost::shared_ptr pimpl_; // Connection parameters + std::string preferredTransferSyntax_; std::string localAet_; std::string distantAet_; std::string distantHost_; @@ -64,7 +65,7 @@ void CheckIsOpen() const; - void SetupPresentationContexts(); + void SetupPresentationContexts(const std::string& preferredTransferSyntax); void Find(DicomFindAnswers& result, FindRootModel model, @@ -115,6 +116,15 @@ return manufacturer_; } + void ResetPreferredTransferSyntax(); + + void SetPreferredTransferSyntax(const std::string& preferredTransferSyntax); + + const std::string& GetPreferredTransferSyntax() const + { + return preferredTransferSyntax_; + } + void Open(); void Close(); diff -r d233b5090105 -r 70161eb45b5c THANKS --- a/THANKS Tue Nov 05 17:41:25 2013 +0100 +++ b/THANKS Wed Nov 06 16:19:25 2013 +0100 @@ -16,7 +16,7 @@ * Will Ryder (will.ryder@sydney.edu.au), for improvements with the handling of series with temporal positions (fMRI and dynamic PET). * Ryan Walklin (ryanwalklin@gmail.com), for Mac OS X build. -* Peter Somlo (peter.somlo@gmail.com), for ClearCanvas support. +* Peter Somlo (peter.somlo@gmail.com), for ClearCanvas and JPEG support. * 12maksqwe@gmail.com, for fixing issue #8. * Julien Nabet, for various suggestions to improve the source code.