# HG changeset patch # User Sebastien Jodogne # Date 1580315971 -3600 # Node ID 8c0ef729d5a86c7bdea2fd4b85fc44fe23712522 # Parent 103ee029982ebbf69199d889ae1504b0c7352992 StorageCommitmentScpJob diff -r 103ee029982e -r 8c0ef729d5a8 Core/DicomNetworking/DicomUserConnection.cpp --- a/Core/DicomNetworking/DicomUserConnection.cpp Wed Jan 29 08:51:31 2020 +0100 +++ b/Core/DicomNetworking/DicomUserConnection.cpp Wed Jan 29 17:39:31 2020 +0100 @@ -1387,23 +1387,46 @@ static void FillSopSequence(DcmDataset& dataset, const DcmTagKey& tag, - const std::vector& sopClassUids, - const std::vector& sopInstanceUids, + const std::list& sopClassUids, + const std::list& sopInstanceUids, bool hasFailureReason, Uint16 failureReason) { - for (size_t i = 0; i < sopClassUids.size(); i++) + assert(sopClassUids.size() == sopInstanceUids.size()); + + if (sopInstanceUids.empty()) { - std::auto_ptr item(new DcmItem); - if (!item->putAndInsertString(DCM_ReferencedSOPClassUID, sopClassUids[i].c_str()).good() || - !item->putAndInsertString(DCM_ReferencedSOPInstanceUID, sopInstanceUids[i].c_str()).good() || - (hasFailureReason && - !item->putAndInsertUint16(DCM_FailureReason, failureReason).good()) || - !dataset.insertSequenceItem(tag, item.release()).good()) + // Add an empty sequence + if (!dataset.insertEmptyElement(tag).good()) { throw OrthancException(ErrorCode_InternalError); } } + else + { + std::list::const_iterator currentClass = sopClassUids.begin(); + std::list::const_iterator currentInstance = sopInstanceUids.begin(); + + while (currentClass != sopClassUids.end()) + { + std::auto_ptr item(new DcmItem); + if (!item->putAndInsertString(DCM_ReferencedSOPClassUID, currentClass->c_str()).good() || + !item->putAndInsertString(DCM_ReferencedSOPInstanceUID, currentInstance->c_str()).good() || + (hasFailureReason && + !item->putAndInsertUint16(DCM_FailureReason, failureReason).good()) || + !dataset.insertSequenceItem(tag, item.release()).good()) + { + throw OrthancException(ErrorCode_InternalError); + } + + ++currentClass; + ++currentInstance; + } + + for (size_t i = 0; i < sopClassUids.size(); i++) + { + } + } } @@ -1411,10 +1434,10 @@ void DicomUserConnection::ReportStorageCommitment( const std::string& transactionUid, - const std::vector& successSopClassUids, - const std::vector& successSopInstanceUids, - const std::vector& failureSopClassUids, - const std::vector& failureSopInstanceUids) + const std::list& successSopClassUids, + const std::list& successSopInstanceUids, + const std::list& failureSopClassUids, + const std::list& failureSopInstanceUids) { if (successSopClassUids.size() != successSopInstanceUids.size() || failureSopClassUids.size() != failureSopInstanceUids.size()) @@ -1551,8 +1574,8 @@ void DicomUserConnection::RequestStorageCommitment( const std::string& transactionUid, - const std::vector& sopClassUids, - const std::vector& sopInstanceUids) + const std::list& sopClassUids, + const std::list& sopInstanceUids) { if (sopClassUids.size() != sopInstanceUids.size()) { diff -r 103ee029982e -r 8c0ef729d5a8 Core/DicomNetworking/DicomUserConnection.h --- a/Core/DicomNetworking/DicomUserConnection.h Wed Jan 29 08:51:31 2020 +0100 +++ b/Core/DicomNetworking/DicomUserConnection.h Wed Jan 29 17:39:31 2020 +0100 @@ -228,15 +228,15 @@ void ReportStorageCommitment( const std::string& transactionUid, - const std::vector& successSopClassUids, - const std::vector& successSopInstanceUids, - const std::vector& failureSopClassUids, - const std::vector& failureSopInstanceUids); + const std::list& successSopClassUids, + const std::list& successSopInstanceUids, + const std::list& failureSopClassUids, + const std::list& failureSopInstanceUids); // transactionUid: To be generated by Toolbox::GenerateDicomPrivateUniqueIdentifier() void RequestStorageCommitment( const std::string& transactionUid, - const std::vector& sopClassUids, - const std::vector& sopInstanceUids); + const std::list& sopClassUids, + const std::list& sopInstanceUids); }; } diff -r 103ee029982e -r 8c0ef729d5a8 Core/JobsEngine/JobsEngine.h --- a/Core/JobsEngine/JobsEngine.h Wed Jan 29 08:51:31 2020 +0100 +++ b/Core/JobsEngine/JobsEngine.h Wed Jan 29 17:39:31 2020 +0100 @@ -39,7 +39,7 @@ namespace Orthanc { - class JobsEngine + class JobsEngine : public boost::noncopyable { private: enum State diff -r 103ee029982e -r 8c0ef729d5a8 OrthancServer/OrthancRestApi/OrthancRestModalities.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Wed Jan 29 08:51:31 2020 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Wed Jan 29 17:39:31 2020 +0100 @@ -1312,7 +1312,7 @@ { DicomUserConnection scu(localAet, remote); - std::vector sopClassUids, sopInstanceUids; + std::list sopClassUids, sopInstanceUids; sopClassUids.push_back("a"); sopInstanceUids.push_back("b"); sopClassUids.push_back("1.2.840.10008.5.1.4.1.1.6.1"); diff -r 103ee029982e -r 8c0ef729d5a8 OrthancServer/main.cpp --- a/OrthancServer/main.cpp Wed Jan 29 08:51:31 2020 +0100 +++ b/OrthancServer/main.cpp Wed Jan 29 17:39:31 2020 +0100 @@ -58,11 +58,11 @@ class OrthancStoreRequestHandler : public IStoreRequestHandler { private: - ServerContext& server_; + ServerContext& context_; public: OrthancStoreRequestHandler(ServerContext& context) : - server_(context) + context_(context) { } @@ -84,66 +84,242 @@ toStore.SetJson(dicomJson); std::string id; - server_.Store(id, toStore); + context_.Store(id, toStore); } } }; -class OrthancStorageCommitmentRequestHandler : public IStorageCommitmentRequestHandler +namespace Orthanc { -private: - ServerContext& server_; + class StorageCommitmentScpJob : public SetOfCommandsJob + { + private: + class LookupCommand : public SetOfCommandsJob::ICommand + { + private: + StorageCommitmentScpJob& that_; + std::string sopClassUid_; + std::string sopInstanceUid_; + + public: + LookupCommand(StorageCommitmentScpJob& that, + const std::string& sopClassUid, + const std::string& sopInstanceUid) : + that_(that), + sopClassUid_(sopClassUid), + sopInstanceUid_(sopInstanceUid) + { + } + + virtual bool Execute() + { + that_.LookupInstance(sopClassUid_, sopInstanceUid_); + return true; + } - // TODO - Remove this - static void Toto(std::string* t, std::string* remotec) - { - try + virtual void Serialize(Json::Value& target) const + { + target = Json::objectValue; + target["Type"] = "Lookup"; + target["SopClassUid"] = sopClassUid_; + target["SopInstanceUid"] = sopInstanceUid_; + } + }; + + class AnswerCommand : public SetOfCommandsJob::ICommand + { + private: + StorageCommitmentScpJob& that_; + + public: + AnswerCommand(StorageCommitmentScpJob& that) : + that_(that) + { + } + + virtual bool Execute() + { + that_.Answer(); + return true; + } + + virtual void Serialize(Json::Value& target) const + { + target = Json::objectValue; + target["Type"] = "Answer"; + } + }; + + class Unserializer : public SetOfCommandsJob::ICommandUnserializer { - std::auto_ptr tt(t); - std::auto_ptr remote(remotec); - - printf("Sleeping\n"); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - printf("Connect back\n"); + private: + StorageCommitmentScpJob& that_; + + public: + Unserializer(StorageCommitmentScpJob& that) : + that_(that) + { + } + + virtual ICommand* Unserialize(const Json::Value& source) const + { + std::cout << "===================================\n"; + std::cout << source.toStyledString(); + + /*DicomMap findAnswer; + findAnswer.Unserialize(source); + return new Command(that_, findAnswer);*/ + + throw OrthancException(ErrorCode_NotImplemented); + } + }; + + ServerContext& context_; + bool ready_; + std::string transactionUid_; + RemoteModalityParameters remoteModality_; + std::string calledAet_; + std::list successSopClassUids_; + std::list successSopInstanceUids_; + std::list failedSopClassUids_; + std::list failedSopInstanceUids_; - RemoteModalityParameters p; + void LookupInstance(const std::string& sopClassUid, + const std::string& sopInstanceUid) + { + bool success = false; + + try + { + std::vector orthancId; + context_.GetIndex().LookupIdentifierExact(orthancId, ResourceType_Instance, DICOM_TAG_SOP_INSTANCE_UID, sopInstanceUid); - if (*remote == "ORTHANC") + if (orthancId.size() == 1) + { + std::string a, b; + + ServerContext::DicomCacheLocker locker(context_, orthancId[0]); + if (locker.GetDicom().GetTagValue(a, DICOM_TAG_SOP_CLASS_UID) && + locker.GetDicom().GetTagValue(b, DICOM_TAG_SOP_INSTANCE_UID) && + a == sopClassUid && + b == sopInstanceUid) + { + success = true; + } + } + } + catch (OrthancException&) { - p = RemoteModalityParameters("ORTHANC", "localhost", 4242, ModalityManufacturer_Generic); + } + + LOG(INFO) << " Storage commitment SCP job: " << (success ? "Success" : "Failure") + << " while looking for " << sopClassUid << " / " << sopInstanceUid; + + if (success) + { + successSopClassUids_.push_back(sopClassUid); + successSopInstanceUids_.push_back(sopInstanceUid); } else { - p = RemoteModalityParameters("STGCMTSCU", "localhost", 11114, ModalityManufacturer_Generic); + failedSopClassUids_.push_back(sopClassUid); + failedSopInstanceUids_.push_back(sopInstanceUid); } - - DicomUserConnection scu("ORTHANC", p); + } + + void Answer() + { + LOG(INFO) << " Storage commitment SCP job: Sending answer"; + + DicomUserConnection scu(calledAet_, remoteModality_); + scu.ReportStorageCommitment(transactionUid_, successSopClassUids_, successSopInstanceUids_, + failedSopClassUids_, failedSopInstanceUids_); - std::vector a, b, c, d; - a.push_back("a"); b.push_back("b"); - a.push_back("c"); b.push_back("d"); + /** + * "After the N-EVENT-REPORT has been sent, the Transaction UID is + * no longer active and shall not be reused for other + * transactions." + * http://dicom.nema.org/medical/dicom/2019a/output/chtml/part04/sect_J.3.3.html + **/ + } - scu.ReportStorageCommitment(tt->c_str(), a, b, c, d); - //scu.ReportStorageCommitment(tt->c_str(), a, b, a, b); - } - catch (OrthancException& e) + public: + StorageCommitmentScpJob(ServerContext& context, + const std::string& transactionUid, + const std::string& remoteAet, + const std::string& calledAet) : + context_(context), + ready_(false), + transactionUid_(transactionUid), + calledAet_(calledAet) { - LOG(ERROR) << "EXCEPTION: " << e.What(); + { + OrthancConfiguration::ReaderLock lock; + if (!lock.GetConfiguration().LookupDicomModalityUsingAETitle(remoteModality_, remoteAet)) + { + throw OrthancException(ErrorCode_InexistentItem, + "Unknown remote modality for storage commitment SCP: " + remoteAet); + } + } } - /** - * "After the N-EVENT-REPORT has been sent, the Transaction UID is - * no longer active and shall not be reused for other - * transactions." - * http://dicom.nema.org/medical/dicom/2019a/output/chtml/part04/sect_J.3.3.html - **/ - } + void AddInstance(const std::string& sopClassUid, + const std::string& sopInstanceUid) + { + if (ready_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + AddCommand(new LookupCommand(*this, sopClassUid, sopInstanceUid)); + } + } + + void MarkAsReady() + { + if (ready_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + AddCommand(new AnswerCommand(*this)); + ready_ = true; + } + } + + virtual void Stop(JobStopReason reason) + { + } + + virtual void GetJobType(std::string& target) + { + target = "StorageCommitmentScp"; + } + + virtual void GetPublicContent(Json::Value& value) + { + SetOfCommandsJob::GetPublicContent(value); + + value["LocalAet"] = calledAet_; + value["RemoteAet"] = remoteModality_.GetApplicationEntityTitle(); + value["TransactionUid"] = transactionUid_; + } + }; +} + + +class OrthancStorageCommitmentRequestHandler : public IStorageCommitmentRequestHandler +{ +private: + ServerContext& context_; public: OrthancStorageCommitmentRequestHandler(ServerContext& context) : - server_(context) + context_(context) { } @@ -154,11 +330,22 @@ const std::string& remoteAet, const std::string& calledAet) { - // TODO - Enqueue a Storage commitment job + if (referencedSopClassUids.size() != referencedSopInstanceUids.size()) + { + throw OrthancException(ErrorCode_InternalError); + } + + std::auto_ptr job( + new StorageCommitmentScpJob(context_, transactionUid, remoteAet, calledAet)); - boost::thread t(Toto, new std::string(transactionUid), new std::string(remoteAet)); + for (size_t i = 0; i < referencedSopClassUids.size(); i++) + { + job->AddInstance(referencedSopClassUids[i], referencedSopInstanceUids[i]); + } - printf("HANDLE REQUEST\n"); + job->MarkAsReady(); + + context_.GetJobsEngine().GetRegistry().Submit(job.release(), 0 /* default priority */); } virtual void HandleReport(const std::string& transactionUid,