Mercurial > hg > orthanc
changeset 1907:5011a597b6ce
Support of Move Originator Message ID (0000,1031) in C-Store responses driven by C-Move
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 07 Jan 2016 11:28:19 +0100 |
parents | d7c1cb559431 |
children | 5096681efce6 |
files | NEWS OrthancServer/DicomProtocol/DicomUserConnection.cpp OrthancServer/DicomProtocol/DicomUserConnection.h OrthancServer/LuaScripting.cpp OrthancServer/OrthancMoveRequestHandler.cpp OrthancServer/OrthancRestApi/OrthancRestModalities.cpp OrthancServer/Scheduler/StoreScuCommand.cpp OrthancServer/Scheduler/StoreScuCommand.h UnitTestsSources/MultiThreadingTests.cpp |
diffstat | 9 files changed, 99 insertions(+), 38 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Tue Jan 05 17:45:27 2016 +0100 +++ b/NEWS Thu Jan 07 11:28:19 2016 +0100 @@ -3,6 +3,7 @@ * Support of optional tags for counting resources in C-Find: 0008-0061, 0008-0062, 0020-1200, 0020-1202, 0020-1204, 0020-1206, 0020-1208, 0020-1209 +* Support of Move Originator Message ID (0000,1031) in C-Store responses driven by C-Move * Huge speedup if decoding the family of JPEG transfer syntaxes * Refactoring leading to speedups with custom image decoders (including Web viewer plugin) * Support decoding of RLE Lossless transfer syntax
--- a/OrthancServer/DicomProtocol/DicomUserConnection.cpp Tue Jan 05 17:45:27 2016 +0100 +++ b/OrthancServer/DicomProtocol/DicomUserConnection.cpp Thu Jan 07 11:28:19 2016 +0100 @@ -148,7 +148,9 @@ void CheckIsOpen() const; - void Store(DcmInputStream& is, DicomUserConnection& connection); + void Store(DcmInputStream& is, + DicomUserConnection& connection, + uint16_t moveMessageID); }; @@ -251,7 +253,9 @@ } - void DicomUserConnection::PImpl::Store(DcmInputStream& is, DicomUserConnection& connection) + void DicomUserConnection::PImpl::Store(DcmInputStream& is, + DicomUserConnection& connection, + uint16_t moveMessageID) { CheckIsOpen(); @@ -325,18 +329,28 @@ } // Prepare the transmission of data - T_DIMSE_C_StoreRQ req; - memset(&req, 0, sizeof(req)); - req.MessageID = assoc_->nextMsgID++; - strcpy(req.AffectedSOPClassUID, sopClass); - strcpy(req.AffectedSOPInstanceUID, sopInstance); - req.DataSetType = DIMSE_DATASET_PRESENT; - req.Priority = DIMSE_PRIORITY_MEDIUM; + T_DIMSE_C_StoreRQ request; + memset(&request, 0, sizeof(request)); + request.MessageID = assoc_->nextMsgID++; + strncpy(request.AffectedSOPClassUID, sopClass, DIC_UI_LEN); + request.Priority = DIMSE_PRIORITY_MEDIUM; + request.DataSetType = DIMSE_DATASET_PRESENT; + strncpy(request.AffectedSOPInstanceUID, sopInstance, DIC_UI_LEN); + + strncpy(request.MoveOriginatorApplicationEntityTitle, + connection.GetLocalApplicationEntityTitle().c_str(), DIC_AE_LEN); + request.opts = O_STORE_MOVEORIGINATORAETITLE; + + if (moveMessageID != 0) + { + request.MoveOriginatorID = moveMessageID; // The type DIC_US is an alias for uint16_t + request.opts |= O_STORE_MOVEORIGINATORID; + } // Finally conduct transmission of data T_DIMSE_C_StoreRSP rsp; DcmDataset* statusDetail = NULL; - Check(DIMSE_storeUser(assoc_, presID, &req, + Check(DIMSE_storeUser(assoc_, presID, &request, NULL, dcmff.getDataset(), /*progressCallback*/ NULL, NULL, /*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ dimseTimeout_, &rsp, &statusDetail, NULL)); @@ -511,9 +525,9 @@ T_DIMSE_C_FindRQ request; memset(&request, 0, sizeof(request)); request.MessageID = association->nextMsgID++; - strcpy(request.AffectedSOPClassUID, sopClass); + strncpy(request.AffectedSOPClassUID, sopClass, DIC_UI_LEN); + request.Priority = DIMSE_PRIORITY_MEDIUM; request.DataSetType = DIMSE_DATASET_PRESENT; - request.Priority = DIMSE_PRIORITY_MEDIUM; T_DIMSE_C_FindRSP response; DcmDataset* statusDetail = NULL; @@ -678,10 +692,10 @@ T_DIMSE_C_MoveRQ request; memset(&request, 0, sizeof(request)); request.MessageID = pimpl_->assoc_->nextMsgID++; - strcpy(request.AffectedSOPClassUID, sopClass); + strncpy(request.AffectedSOPClassUID, sopClass, DIC_UI_LEN); + request.Priority = DIMSE_PRIORITY_MEDIUM; request.DataSetType = DIMSE_DATASET_PRESENT; - request.Priority = DIMSE_PRIORITY_MEDIUM; - strncpy(request.MoveDestination, targetAet.c_str(), sizeof(DIC_AE) / sizeof(char)); + strncpy(request.MoveDestination, targetAet.c_str(), DIC_AE_LEN); T_DIMSE_C_MoveRSP response; DcmDataset* statusDetail = NULL; @@ -920,7 +934,9 @@ return pimpl_->IsOpen(); } - void DicomUserConnection::Store(const char* buffer, size_t size) + void DicomUserConnection::Store(const char* buffer, + size_t size, + uint16_t moveMessageID) { // Prepare an input stream for the memory buffer DcmInputBufferStream is; @@ -928,22 +944,24 @@ is.setBuffer(buffer, size); is.setEos(); - pimpl_->Store(is, *this); + pimpl_->Store(is, *this, moveMessageID); } - void DicomUserConnection::Store(const std::string& buffer) + void DicomUserConnection::Store(const std::string& buffer, + uint16_t moveMessageID) { if (buffer.size() > 0) - Store(reinterpret_cast<const char*>(&buffer[0]), buffer.size()); + Store(reinterpret_cast<const char*>(&buffer[0]), buffer.size(), moveMessageID); else - Store(NULL, 0); + Store(NULL, 0, moveMessageID); } - void DicomUserConnection::StoreFile(const std::string& path) + void DicomUserConnection::StoreFile(const std::string& path, + uint16_t moveMessageID) { // Prepare an input stream for the file DcmInputFileStream is(path.c_str()); - pimpl_->Store(is, *this); + pimpl_->Store(is, *this, moveMessageID); } bool DicomUserConnection::Echo()
--- a/OrthancServer/DicomProtocol/DicomUserConnection.h Tue Jan 05 17:45:27 2016 +0100 +++ b/OrthancServer/DicomProtocol/DicomUserConnection.h Thu Jan 07 11:28:19 2016 +0100 @@ -133,11 +133,15 @@ bool Echo(); - void Store(const char* buffer, size_t size); + void Store(const char* buffer, + size_t size, + uint16_t moveMessageID); - void Store(const std::string& buffer); + void Store(const std::string& buffer, + uint16_t moveMessageID); - void StoreFile(const std::string& path); + void StoreFile(const std::string& path, + uint16_t moveMessageID); void Find(DicomFindAnswers& result, ResourceType level,
--- a/OrthancServer/LuaScripting.cpp Tue Jan 05 17:45:27 2016 +0100 +++ b/OrthancServer/LuaScripting.cpp Thu Jan 07 11:28:19 2016 +0100 @@ -253,7 +253,8 @@ LOG(INFO) << "Lua script to send resource " << parameters["Resource"].asString() << " to modality " << modality << " using Store-SCU"; return new StoreScuCommand(context_, localAet, - Configuration::GetModalityUsingSymbolicName(modality), true); + Configuration::GetModalityUsingSymbolicName(modality), + true, 0 /* not a C-MOVE */); } if (operation == "store-peer")
--- a/OrthancServer/OrthancMoveRequestHandler.cpp Tue Jan 05 17:45:27 2016 +0100 +++ b/OrthancServer/OrthancMoveRequestHandler.cpp Thu Jan 07 11:28:19 2016 +0100 @@ -52,14 +52,17 @@ std::vector<std::string> instances_; size_t position_; RemoteModalityParameters remote_; + uint16_t moveRequestID_; public: OrthancMoveRequestIterator(ServerContext& context, const std::string& aet, - const std::string& publicId) : + const std::string& publicId, + uint16_t moveRequestID) : context_(context), localAet_(context.GetDefaultLocalApplicationEntityTitle()), - position_(0) + position_(0), + moveRequestID_(moveRequestID) { LOG(INFO) << "Sending resource " << publicId << " to modality \"" << aet << "\""; @@ -95,7 +98,7 @@ { ReusableDicomUserConnection::Locker locker (context_.GetReusableDicomUserConnection(), localAet_, remote_); - locker.GetConnection().Store(dicom); + locker.GetConnection().Store(dicom, moveRequestID_); } return Status_Success; @@ -184,6 +187,33 @@ } + /** + * Retrieve the Message ID (0000,0110) for this C-MOVE request, if + * any. If present, this Message ID will be stored in the Move + * Originator Message ID (0000,1031) field of the C-MOVE response. + * http://dicom.nema.org/medical/dicom/current/output/html/part07.html#sect_9.3.1 + **/ + + static const DicomTag MESSAGE_ID(0x0000, 0x0110); + const DicomValue* messageIdTmp = input.TestAndGetValue(MESSAGE_ID); + + uint16_t messageId = 0; + + if (messageIdTmp != NULL && + !messageIdTmp->IsNull() && + !messageIdTmp->IsBinary()) + { + try + { + messageId = boost::lexical_cast<uint16_t>(messageIdTmp->GetContent()); + } + catch (boost::bad_lexical_cast&) + { + LOG(WARNING) << "Cannot convert the Message ID (\"" << messageIdTmp ->GetContent() + << "\") of an incoming C-MOVE request to an integer, assuming zero"; + } + } + /** * Retrieve the query level. @@ -208,7 +238,7 @@ LookupIdentifier(publicId, ResourceType_Study, input) || LookupIdentifier(publicId, ResourceType_Patient, input)) { - return new OrthancMoveRequestIterator(context_, targetAet, publicId); + return new OrthancMoveRequestIterator(context_, targetAet, publicId, messageId); } else { @@ -229,7 +259,7 @@ if (LookupIdentifier(publicId, level, input)) { - return new OrthancMoveRequestIterator(context_, targetAet, publicId); + return new OrthancMoveRequestIterator(context_, targetAet, publicId, messageId); } else {
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Tue Jan 05 17:45:27 2016 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Thu Jan 07 11:28:19 2016 +0100 @@ -695,7 +695,8 @@ for (std::list<std::string>::const_iterator it = instances.begin(); it != instances.end(); ++it) { - job.AddCommand(new StoreScuCommand(context, localAet, p, false)).AddInput(*it); + job.AddCommand(new StoreScuCommand(context, localAet, p, false, + 0 /* not a C-MOVE */)).AddInput(*it); } job.SetDescription("HTTP request: Store-SCU to peer \"" + remote + "\"");
--- a/OrthancServer/Scheduler/StoreScuCommand.cpp Tue Jan 05 17:45:27 2016 +0100 +++ b/OrthancServer/Scheduler/StoreScuCommand.cpp Thu Jan 07 11:28:19 2016 +0100 @@ -40,11 +40,13 @@ StoreScuCommand::StoreScuCommand(ServerContext& context, const std::string& localAet, const RemoteModalityParameters& modality, - bool ignoreExceptions) : + bool ignoreExceptions, + uint16_t moveMessageID) : context_(context), modality_(modality), ignoreExceptions_(ignoreExceptions), - localAet_(localAet) + localAet_(localAet), + moveMessageID_(moveMessageID) { } @@ -63,7 +65,8 @@ { std::string dicom; context_.ReadFile(dicom, *it, FileContentType_Dicom); - locker.GetConnection().Store(dicom); + + locker.GetConnection().Store(dicom, moveMessageID_); // Only chain with other commands if this command succeeds outputs.push_back(*it);
--- a/OrthancServer/Scheduler/StoreScuCommand.h Tue Jan 05 17:45:27 2016 +0100 +++ b/OrthancServer/Scheduler/StoreScuCommand.h Thu Jan 07 11:28:19 2016 +0100 @@ -44,12 +44,15 @@ RemoteModalityParameters modality_; bool ignoreExceptions_; std::string localAet_; + uint16_t moveMessageID_; public: StoreScuCommand(ServerContext& context, const std::string& localAet, const RemoteModalityParameters& modality, - bool ignoreExceptions); + bool ignoreExceptions, + uint16_t moveMessageID /* only makes sense if this + command results from a C-MOVE */); virtual bool Apply(ListOfStrings& outputs, const ListOfStrings& inputs);
--- a/UnitTestsSources/MultiThreadingTests.cpp Tue Jan 05 17:45:27 2016 +0100 +++ b/UnitTestsSources/MultiThreadingTests.cpp Thu Jan 07 11:28:19 2016 +0100 @@ -138,7 +138,7 @@ { RemoteModalityParameters remote("STORESCP", "localhost", 2000, ModalityManufacturer_Generic); ReusableDicomUserConnection::Locker lock(c, "ORTHANC", remote); - lock.GetConnection().StoreFile("/home/jodogne/DICOM/Cardiac/MR.X.1.2.276.0.7230010.3.1.4.2831157719.2256.1336386844.676281"); + lock.GetConnection().StoreFile("/home/jodogne/DICOM/Cardiac/MR.X.1.2.276.0.7230010.3.1.4.2831157719.2256.1336386844.676281", 0); } printf("**\n"); fflush(stdout); @@ -148,7 +148,7 @@ { RemoteModalityParameters remote("STORESCP", "localhost", 2000, ModalityManufacturer_Generic); ReusableDicomUserConnection::Locker lock(c, "ORTHANC", remote); - lock.GetConnection().StoreFile("/home/jodogne/DICOM/Cardiac/MR.X.1.2.276.0.7230010.3.1.4.2831157719.2256.1336386844.676277"); + lock.GetConnection().StoreFile("/home/jodogne/DICOM/Cardiac/MR.X.1.2.276.0.7230010.3.1.4.2831157719.2256.1336386844.676277", 0); } Toolbox::ServerBarrier();