# HG changeset patch # User Sebastien Jodogne # Date 1583868781 -3600 # Node ID f29843323daf952cea764a9b777d16559411c968 # Parent 0540b54324f19180c6e9c3fa24f368d21ac9b303 accessing storage commitment reports from REST API diff -r 0540b54324f1 -r f29843323daf Core/Enumerations.cpp --- a/Core/Enumerations.cpp Tue Mar 10 17:43:49 2020 +0100 +++ b/Core/Enumerations.cpp Tue Mar 10 20:33:01 2020 +0100 @@ -1168,6 +1168,41 @@ } + const char* EnumerationToString(StorageCommitmentFailureReason reason) + { + switch (reason) + { + case StorageCommitmentFailureReason_Success: + return "Success"; + + case StorageCommitmentFailureReason_ProcessingFailure: + return "A general failure in processing the operation was encountered"; + + case StorageCommitmentFailureReason_NoSuchObjectInstance: + return "One or more of the elements in the Referenced SOP " + "Instance Sequence was not available"; + + case StorageCommitmentFailureReason_ResourceLimitation: + return "The SCP does not currently have enough resources to " + "store the requested SOP Instance(s)"; + + case StorageCommitmentFailureReason_ReferencedSOPClassNotSupported: + return "Storage Commitment has been requested for a SOP Instance " + "with a SOP Class that is not supported by the SCP"; + + case StorageCommitmentFailureReason_ClassInstanceConflict: + return "The SOP Class of an element in the Referenced SOP Instance Sequence " + "did not correspond to the SOP class registered for this SOP Instance at the SCP"; + + case StorageCommitmentFailureReason_DuplicateTransactionUID: + return "The Transaction UID of the Storage Commitment Request is already in use"; + + default: + return "Unknown failure reason"; + } + } + + Encoding StringToEncoding(const char* encoding) { std::string s(encoding); diff -r 0540b54324f1 -r f29843323daf Core/Enumerations.h --- a/Core/Enumerations.h Tue Mar 10 17:43:49 2020 +0100 +++ b/Core/Enumerations.h Tue Mar 10 20:33:01 2020 +0100 @@ -742,7 +742,7 @@ StorageCommitmentFailureReason_ClassInstanceConflict = 0x0119, // The Transaction UID of the Storage Commitment Request is already in use - StorageCommitmentFailureReason_DuplicateTransactionUID = 0x0131 + StorageCommitmentFailureReason_DuplicateTransactionUID = 0x0131 }; @@ -831,6 +831,8 @@ const char* EnumerationToString(Endianness endianness); + const char* EnumerationToString(StorageCommitmentFailureReason reason); + Encoding StringToEncoding(const char* encoding); ResourceType StringToResourceType(const char* type); diff -r 0540b54324f1 -r f29843323daf OrthancServer/OrthancRestApi/OrthancRestModalities.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Tue Mar 10 17:43:49 2020 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Tue Mar 10 20:33:01 2020 +0100 @@ -46,6 +46,7 @@ #include "../ServerJobs/DicomMoveScuJob.h" #include "../ServerJobs/OrthancPeerStoreJob.h" #include "../ServerToolbox.h" +#include "../StorageCommitmentReports.h" namespace Orthanc @@ -1305,7 +1306,9 @@ } - static void StorageCommitment(RestApiPostCall& call) + // Storage commitment SCU --------------------------------------------------- + + static void StorageCommitmentScu(RestApiPostCall& call) { ServerContext& context = OrthancRestApi::GetContext(call); @@ -1364,20 +1367,27 @@ } } - const std::string transaction = Toolbox::GenerateDicomPrivateUniqueIdentifier(); + const std::string transactionUid = Toolbox::GenerateDicomPrivateUniqueIdentifier(); { - const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); const RemoteModalityParameters remote = MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); + const std::string& remoteAet = remote.GetApplicationEntityTitle(); + const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); + + // Create a "pending" storage commitment report BEFORE the + // actual SCU call in order to avoid race conditions + context.GetStorageCommitmentReports().Store( + transactionUid, new StorageCommitmentReports::Report(remoteAet)); + DicomUserConnection scu(localAet, remote); - scu.RequestStorageCommitment(transaction, sopClassUids, sopInstanceUids); + scu.RequestStorageCommitment(transactionUid, sopClassUids, sopInstanceUids); } Json::Value result = Json::objectValue; - result["ID"] = transaction; - result["Path"] = "/storage-commitment/" + transaction; + result["ID"] = transactionUid; + result["Path"] = "/storage-commitment/" + transactionUid; call.GetOutput().AnswerJson(result); } else @@ -1388,6 +1398,31 @@ } + static void GetStorageCommitmentReport(RestApiGetCall& call) + { + ServerContext& context = OrthancRestApi::GetContext(call); + + const std::string& transactionUid = call.GetUriComponent("id", ""); + + { + StorageCommitmentReports::Accessor accessor( + context.GetStorageCommitmentReports(), transactionUid); + + if (accessor.IsValid()) + { + Json::Value json; + accessor.GetReport().Format(json); + call.GetOutput().AnswerJson(json); + } + else + { + throw OrthancException(ErrorCode_InexistentItem, + "No storage commitment transaction with UID: " + transactionUid); + } + } + } + + void OrthancRestApi::RegisterModalities() { Register("/modalities", ListModalities); @@ -1432,6 +1467,7 @@ Register("/modalities/{id}/find-worklist", DicomFindWorklist); - Register("/modalities/{id}/storage-commitment", StorageCommitment); + Register("/modalities/{id}/storage-commitment", StorageCommitmentScu); + Register("/storage-commitment/{id}", GetStorageCommitmentReport); } } diff -r 0540b54324f1 -r f29843323daf OrthancServer/ServerJobs/DicomModalityStoreJob.cpp --- a/OrthancServer/ServerJobs/DicomModalityStoreJob.cpp Tue Mar 10 17:43:49 2020 +0100 +++ b/OrthancServer/ServerJobs/DicomModalityStoreJob.cpp Tue Mar 10 20:33:01 2020 +0100 @@ -38,6 +38,7 @@ #include "../../Core/Logging.h" #include "../../Core/SerializationToolbox.h" #include "../ServerContext.h" +#include "../StorageCommitmentReports.h" namespace Orthanc @@ -96,9 +97,15 @@ } if (sopClassUids_.size() == GetInstancesCount()) - { - LOG(INFO) << "Sending storage commitment request to modality: " - << remote_.GetApplicationEntityTitle(); + { + const std::string& remoteAet = remote_.GetApplicationEntityTitle(); + + LOG(INFO) << "Sending storage commitment request to modality: " << remoteAet; + + // Create a "pending" storage commitment report BEFORE the + // actual SCU call in order to avoid race conditions + context_.GetStorageCommitmentReports().Store( + transactionUid_, new StorageCommitmentReports::Report(remoteAet)); assert(IsStarted()); OpenConnection(); diff -r 0540b54324f1 -r f29843323daf OrthancServer/StorageCommitmentReports.cpp --- a/OrthancServer/StorageCommitmentReports.cpp Tue Mar 10 17:43:49 2020 +0100 +++ b/OrthancServer/StorageCommitmentReports.cpp Tue Mar 10 20:33:01 2020 +0100 @@ -62,7 +62,7 @@ Success success; success.sopClassUid_ = sopClassUid; success.sopInstanceUid_ = sopInstanceUid; - successes_.push_back(success); + success_.push_back(success); } } @@ -102,6 +102,71 @@ } + void StorageCommitmentReports::Report::Format(Json::Value& json) const + { + static const char* const FIELD_STATUS = "Status"; + static const char* const FIELD_SOP_CLASS_UID = "SOPClassUID"; + static const char* const FIELD_SOP_INSTANCE_UID = "SOPInstanceUID"; + static const char* const FIELD_FAILURE_REASON = "FailureReason"; + static const char* const FIELD_DESCRIPTION = "Description"; + static const char* const FIELD_REMOTE_AET = "RemoteAET"; + static const char* const FIELD_SUCCESS = "Success"; + static const char* const FIELD_FAILURES = "Failures"; + + + json = Json::objectValue; + json[FIELD_REMOTE_AET] = remoteAet_; + + switch (GetStatus()) + { + case Status_Pending: + json[FIELD_STATUS] = "Pending"; + break; + + case Status_Success: + json[FIELD_STATUS] = "Success"; + break; + + case Status_Failure: + json[FIELD_STATUS] = "Failure"; + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + { + Json::Value success = Json::arrayValue; + for (std::list::const_iterator + it = success_.begin(); it != success_.end(); ++it) + { + Json::Value item = Json::objectValue; + item[FIELD_SOP_CLASS_UID] = it->sopClassUid_; + item[FIELD_SOP_INSTANCE_UID] = it->sopInstanceUid_; + success.append(item); + } + + json[FIELD_SUCCESS] = success; + } + + { + Json::Value failures = Json::arrayValue; + for (std::list::const_iterator + it = failures_.begin(); it != failures_.end(); ++it) + { + Json::Value item = Json::objectValue; + item[FIELD_SOP_CLASS_UID] = it->sopClassUid_; + item[FIELD_SOP_INSTANCE_UID] = it->sopInstanceUid_; + item[FIELD_FAILURE_REASON] = it->reason_; + item[FIELD_DESCRIPTION] = EnumerationToString(it->reason_); + failures.append(item); + } + + json[FIELD_FAILURES] = failures; + } + } + + StorageCommitmentReports::~StorageCommitmentReports() { while (!content_.IsEmpty()) diff -r 0540b54324f1 -r f29843323daf OrthancServer/StorageCommitmentReports.h --- a/OrthancServer/StorageCommitmentReports.h Tue Mar 10 17:43:49 2020 +0100 +++ b/OrthancServer/StorageCommitmentReports.h Tue Mar 10 20:33:01 2020 +0100 @@ -65,7 +65,7 @@ }; bool isComplete_; - std::list successes_; + std::list success_; std::list failures_; std::string remoteAet_; @@ -91,6 +91,8 @@ StorageCommitmentFailureReason reason); Status GetStatus() const; + + void Format(Json::Value& json) const; }; private: