Mercurial > hg > orthanc
changeset 1427:d710ea64f0fd
Custom setting of the local AET during C-Store SCU (both in Lua and in the REST API)
line wrap: on
line diff
--- a/NEWS Fri Jun 26 16:30:51 2015 +0200 +++ b/NEWS Mon Jun 29 12:42:54 2015 +0200 @@ -2,6 +2,7 @@ =============================== * The configuration can be splitted into several files stored inside the same folder +* Custom setting of the local AET during C-Store SCU (both in Lua and in the REST API) * Plugins can retrieve the configuration file directly as a JSON string * Fix issue 35 (Characters in PatientID string are not protected for C-Find) * Fix issue 37 (Hyphens trigger range query even if datatype does not support ranges)
--- a/OrthancServer/DicomProtocol/DicomUserConnection.cpp Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/DicomProtocol/DicomUserConnection.cpp Mon Jun 29 12:42:54 2015 +0200 @@ -679,7 +679,7 @@ } - void DicomUserConnection::Connect(const RemoteModalityParameters& parameters) + void DicomUserConnection::SetRemoteModality(const RemoteModalityParameters& parameters) { SetRemoteApplicationEntityTitle(parameters.GetApplicationEntityTitle()); SetRemoteHost(parameters.GetHost());
--- a/OrthancServer/DicomProtocol/DicomUserConnection.h Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/DicomProtocol/DicomUserConnection.h Mon Jun 29 12:42:54 2015 +0200 @@ -77,7 +77,7 @@ ~DicomUserConnection(); - void Connect(const RemoteModalityParameters& parameters); + void SetRemoteModality(const RemoteModalityParameters& parameters); void SetLocalApplicationEntityTitle(const std::string& aet);
--- a/OrthancServer/DicomProtocol/RemoteModalityParameters.cpp Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/DicomProtocol/RemoteModalityParameters.cpp Mon Jun 29 12:42:54 2015 +0200 @@ -48,6 +48,18 @@ { } + RemoteModalityParameters::RemoteModalityParameters(const std::string& aet, + const std::string& host, + int port, + ModalityManufacturer manufacturer) + { + SetApplicationEntityTitle(aet); + SetHost(host); + SetPort(port); + SetManufacturer(manufacturer); + } + + void RemoteModalityParameters::SetPort(int port) { if (port <= 0 || port >= 65535)
--- a/OrthancServer/DicomProtocol/RemoteModalityParameters.h Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/DicomProtocol/RemoteModalityParameters.h Mon Jun 29 12:42:54 2015 +0200 @@ -41,8 +41,6 @@ { class RemoteModalityParameters { - // TODO Use the flyweight pattern for this class - private: std::string aet_; std::string host_; @@ -52,6 +50,11 @@ public: RemoteModalityParameters(); + RemoteModalityParameters(const std::string& aet, + const std::string& host, + int port, + ModalityManufacturer manufacturer); + const std::string& GetApplicationEntityTitle() const { return aet_;
--- a/OrthancServer/DicomProtocol/ReusableDicomUserConnection.cpp Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/DicomProtocol/ReusableDicomUserConnection.cpp Mon Jun 29 12:42:54 2015 +0200 @@ -44,16 +44,15 @@ return boost::posix_time::microsec_clock::local_time(); } - void ReusableDicomUserConnection::Open(const std::string& remoteAet, - const std::string& address, - int port, - ModalityManufacturer manufacturer) + void ReusableDicomUserConnection::Open(const std::string& localAet, + const RemoteModalityParameters& remote) { if (connection_ != NULL && - connection_->GetRemoteApplicationEntityTitle() == remoteAet && - connection_->GetRemoteHost() == address && - connection_->GetRemotePort() == port && - connection_->GetRemoteManufacturer() == manufacturer) + connection_->GetLocalApplicationEntityTitle() == localAet && + connection_->GetRemoteApplicationEntityTitle() == remote.GetApplicationEntityTitle() && + connection_->GetRemoteHost() == remote.GetHost() && + connection_->GetRemotePort() == remote.GetPort() && + connection_->GetRemoteManufacturer() == remote.GetManufacturer()) { // The current connection can be reused LOG(INFO) << "Reusing the previous SCU connection"; @@ -63,11 +62,8 @@ Close(); connection_ = new DicomUserConnection(); - connection_->SetLocalApplicationEntityTitle(localAet_); - connection_->SetRemoteApplicationEntityTitle(remoteAet); - connection_->SetRemoteHost(address); - connection_->SetRemotePort(port); - connection_->SetRemoteManufacturer(manufacturer); + connection_->SetLocalApplicationEntityTitle(localAet); + connection_->SetRemoteModality(remote); connection_->Open(); } @@ -103,24 +99,13 @@ } } - ReusableDicomUserConnection::Locker::Locker(ReusableDicomUserConnection& that, - const std::string& aet, - const std::string& address, - int port, - ModalityManufacturer manufacturer) : - ::Orthanc::Locker(that) - { - that.Open(aet, address, port, manufacturer); - connection_ = that.connection_; - } - ReusableDicomUserConnection::Locker::Locker(ReusableDicomUserConnection& that, + const std::string& localAet, const RemoteModalityParameters& remote) : ::Orthanc::Locker(that) { - that.Open(remote.GetApplicationEntityTitle(), remote.GetHost(), - remote.GetPort(), remote.GetManufacturer()); + that.Open(localAet, remote); connection_ = that.connection_; } @@ -137,8 +122,7 @@ ReusableDicomUserConnection::ReusableDicomUserConnection() : connection_(NULL), - timeBeforeClose_(boost::posix_time::seconds(5)), // By default, close connection after 5 seconds - localAet_("ORTHANC") + timeBeforeClose_(boost::posix_time::seconds(5)) // By default, close connection after 5 seconds { lastUse_ = Now(); continue_ = true; @@ -164,13 +148,6 @@ timeBeforeClose_ = boost::posix_time::milliseconds(ms); } - void ReusableDicomUserConnection::SetLocalApplicationEntityTitle(const std::string& aet) - { - boost::mutex::scoped_lock lock(mutex_); - Close(); - localAet_ = aet; - } - void ReusableDicomUserConnection::Lock() { mutex_.lock();
--- a/OrthancServer/DicomProtocol/ReusableDicomUserConnection.h Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/DicomProtocol/ReusableDicomUserConnection.h Mon Jun 29 12:42:54 2015 +0200 @@ -49,12 +49,9 @@ boost::posix_time::time_duration timeBeforeClose_; boost::posix_time::ptime lastUse_; boost::thread closeThread_; - std::string localAet_; - void Open(const std::string& remoteAet, - const std::string& address, - int port, - ModalityManufacturer manufacturer); + void Open(const std::string& localAet, + const RemoteModalityParameters& remote); void Close(); @@ -73,14 +70,9 @@ public: Locker(ReusableDicomUserConnection& that, + const std::string& localAet, const RemoteModalityParameters& remote); - Locker(ReusableDicomUserConnection& that, - const std::string& aet, - const std::string& address, - int port, - ModalityManufacturer manufacturer); - DicomUserConnection& GetConnection(); }; @@ -88,16 +80,7 @@ virtual ~ReusableDicomUserConnection(); - uint64_t GetMillisecondsBeforeClose() const - { - return static_cast<uint64_t>(timeBeforeClose_.total_milliseconds()); - } - void SetMillisecondsBeforeClose(uint64_t ms); - - const std::string& GetLocalApplicationEntityTitle() const; - - void SetLocalApplicationEntityTitle(const std::string& aet); }; }
--- a/OrthancServer/OrthancInitialization.cpp Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/OrthancInitialization.cpp Mon Jun 29 12:42:54 2015 +0200 @@ -149,7 +149,7 @@ if (!boost::filesystem::exists(configurationFile)) { LOG(ERROR) << "Inexistent path to configuration: " << configurationFile; - return; + throw OrthancException(ErrorCode_InexistentFile); } if (boost::filesystem::is_directory(configurationFile))
--- a/OrthancServer/OrthancMoveRequestHandler.cpp Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/OrthancMoveRequestHandler.cpp Mon Jun 29 12:42:54 2015 +0200 @@ -49,6 +49,7 @@ { private: ServerContext& context_; + const std::string& localAet_; std::vector<std::string> instances_; size_t position_; RemoteModalityParameters remote_; @@ -58,6 +59,7 @@ const std::string& aet, const std::string& publicId) : context_(context), + localAet_(context.GetDefaultLocalApplicationEntityTitle()), position_(0) { LOG(INFO) << "Sending resource " << publicId << " to modality \"" << aet << "\""; @@ -93,7 +95,7 @@ { ReusableDicomUserConnection::Locker locker - (context_.GetReusableDicomUserConnection(), remote_); + (context_.GetReusableDicomUserConnection(), localAet_, remote_); locker.GetConnection().Store(dicom); }
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Mon Jun 29 12:42:54 2015 +0200 @@ -54,8 +54,9 @@ { ServerContext& context = OrthancRestApi::GetContext(call); + const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); - ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), remote); + ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote); try { @@ -173,8 +174,9 @@ return; } + const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); - ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), remote); + ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote); DicomFindAnswers answers; FindPatient(answers, locker.GetConnection(), fields); @@ -202,8 +204,9 @@ return; } + const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); - ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), remote); + ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote); DicomFindAnswers answers; FindStudy(answers, locker.GetConnection(), fields); @@ -232,8 +235,9 @@ return; } + const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); - ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), remote); + ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote); DicomFindAnswers answers; FindSeries(answers, locker.GetConnection(), fields); @@ -263,8 +267,9 @@ return; } + const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); - ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), remote); + ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote); DicomFindAnswers answers; FindInstance(answers, locker.GetConnection(), fields); @@ -287,8 +292,9 @@ return; } + const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); - ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), remote); + ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote); DicomFindAnswers patients; FindPatient(patients, locker.GetConnection(), m); @@ -547,7 +553,8 @@ * DICOM C-Store SCU ***************************************************************************/ - static bool GetInstancesToExport(std::list<std::string>& instances, + static bool GetInstancesToExport(Json::Value& request, + std::list<std::string>& instances, const std::string& remote, RestApiPostCall& call) { @@ -555,7 +562,7 @@ std::string stripped = Toolbox::StripSpaces(call.GetPostBody()); - Json::Value request; + request = Json::objectValue; if (Toolbox::IsSHA1(stripped)) { // This is for compatibility with Orthanc <= 0.5.1. @@ -575,41 +582,55 @@ } context.GetIndex().GetChildInstances(instances, request.asString()); + return true; } - else if (request.isArray()) - { - for (Json::Value::ArrayIndex i = 0; i < request.size(); i++) - { - if (!request[i].isString()) - { - return false; - } - std::string stripped = Toolbox::StripSpaces(request[i].asString()); - if (!Toolbox::IsSHA1(stripped)) - { - return false; - } - - if (Configuration::GetGlobalBoolParameter("LogExportedResources", true)) - { - context.GetIndex().LogExportedResource(stripped, remote); - } - - std::list<std::string> tmp; - context.GetIndex().GetChildInstances(tmp, stripped); - - for (std::list<std::string>::const_iterator - it = tmp.begin(); it != tmp.end(); ++it) - { - instances.push_back(*it); - } - } + const Json::Value* resources; + if (request.isArray()) + { + resources = &request; } else { - // Neither a string, nor a list of strings. Bad request. - return false; + if (request.type() != Json::objectValue || + !request.isMember("Resources")) + { + return false; + } + + resources = &request["Resources"]; + if (!resources->isArray()) + { + return false; + } + } + + for (Json::Value::ArrayIndex i = 0; i < resources->size(); i++) + { + if (!(*resources) [i].isString()) + { + return false; + } + + std::string stripped = Toolbox::StripSpaces((*resources) [i].asString()); + if (!Toolbox::IsSHA1(stripped)) + { + return false; + } + + if (Configuration::GetGlobalBoolParameter("LogExportedResources", true)) + { + context.GetIndex().LogExportedResource(stripped, remote); + } + + std::list<std::string> tmp; + context.GetIndex().GetChildInstances(tmp, stripped); + + for (std::list<std::string>::const_iterator + it = tmp.begin(); it != tmp.end(); ++it) + { + instances.push_back(*it); + } } return true; @@ -622,19 +643,26 @@ std::string remote = call.GetUriComponent("id", ""); + Json::Value request; std::list<std::string> instances; - if (!GetInstancesToExport(instances, remote, call)) + if (!GetInstancesToExport(request, instances, remote, call)) { return; } + std::string localAet = context.GetDefaultLocalApplicationEntityTitle(); + if (request.isMember("LocalAet")) + { + localAet = request["LocalAet"].asString(); + } + RemoteModalityParameters p = Configuration::GetModalityUsingSymbolicName(remote); ServerJob job; for (std::list<std::string>::const_iterator it = instances.begin(); it != instances.end(); ++it) { - job.AddCommand(new StoreScuCommand(context, p, false)).AddInput(*it); + job.AddCommand(new StoreScuCommand(context, localAet, p, false)).AddInput(*it); } job.SetDescription("HTTP request: Store-SCU to peer \"" + remote + "\""); @@ -694,8 +722,9 @@ std::string remote = call.GetUriComponent("id", ""); + Json::Value request; std::list<std::string> instances; - if (!GetInstancesToExport(instances, remote, call)) + if (!GetInstancesToExport(request, instances, remote, call)) { return; }
--- a/OrthancServer/QueryRetrieveHandler.cpp Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/QueryRetrieveHandler.cpp Mon Jun 29 12:42:54 2015 +0200 @@ -49,7 +49,7 @@ { if (!done_) { - ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), modality_); + ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), localAet_, modality_); locker.GetConnection().Find(answers_, level_, query_); done_ = true; } @@ -58,6 +58,7 @@ QueryRetrieveHandler::QueryRetrieveHandler(ServerContext& context) : context_(context), + localAet_(context.GetDefaultLocalApplicationEntityTitle()), done_(false), level_(ResourceType_Study) { @@ -117,7 +118,7 @@ throw OrthancException(ErrorCode_ParameterOutOfRange); } - ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), modality_); + ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), localAet_, modality_); locker.GetConnection().Move(target, answers_.GetAnswer(i)); }
--- a/OrthancServer/QueryRetrieveHandler.h Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/QueryRetrieveHandler.h Mon Jun 29 12:42:54 2015 +0200 @@ -40,6 +40,7 @@ { private: ServerContext& context_; + const std::string& localAet_; bool done_; RemoteModalityParameters modality_; ResourceType level_;
--- a/OrthancServer/Scheduler/StoreScuCommand.cpp Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/Scheduler/StoreScuCommand.cpp Mon Jun 29 12:42:54 2015 +0200 @@ -37,18 +37,20 @@ namespace Orthanc { StoreScuCommand::StoreScuCommand(ServerContext& context, + const std::string& localAet, const RemoteModalityParameters& modality, bool ignoreExceptions) : context_(context), modality_(modality), - ignoreExceptions_(ignoreExceptions) + ignoreExceptions_(ignoreExceptions), + localAet_(localAet) { } bool StoreScuCommand::Apply(ListOfStrings& outputs, const ListOfStrings& inputs) { - ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), modality_); + ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), localAet_, modality_); for (ListOfStrings::const_iterator it = inputs.begin(); it != inputs.end(); ++it)
--- a/OrthancServer/Scheduler/StoreScuCommand.h Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/Scheduler/StoreScuCommand.h Mon Jun 29 12:42:54 2015 +0200 @@ -43,9 +43,11 @@ ServerContext& context_; RemoteModalityParameters modality_; bool ignoreExceptions_; + std::string localAet_; public: StoreScuCommand(ServerContext& context, + const std::string& localAet, const RemoteModalityParameters& modality, bool ignoreExceptions);
--- a/OrthancServer/ServerContext.cpp Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/ServerContext.cpp Mon Jun 29 12:42:54 2015 +0200 @@ -79,10 +79,9 @@ scheduler_(Configuration::GetGlobalIntegerParameter("LimitJobs", 10)), plugins_(NULL), pluginsManager_(NULL), - queryRetrieveArchive_(Configuration::GetGlobalIntegerParameter("QueryRetrieveSize", 10)) + queryRetrieveArchive_(Configuration::GetGlobalIntegerParameter("QueryRetrieveSize", 10)), + defaultLocalAet_(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")) { - scu_.SetLocalApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")); - uint64_t s = Configuration::GetGlobalIntegerParameter("DicomAssociationCloseDelay", 5); // In seconds scu_.SetMillisecondsBeforeClose(s * 1000); // Milliseconds are expected here @@ -140,10 +139,21 @@ if (operation == "store-scu") { + std::string localAet; + if (parameters.isMember("LocalAet")) + { + localAet = parameters["LocalAet"].asString(); + } + else + { + localAet = context.GetDefaultLocalApplicationEntityTitle(); + } + std::string modality = parameters["Modality"].asString(); LOG(INFO) << "Lua script to send instance " << parameters["Instance"].asString() << " to modality " << modality << " using Store-SCU"; - return new StoreScuCommand(context, Configuration::GetModalityUsingSymbolicName(modality), true); + return new StoreScuCommand(context, localAet, + Configuration::GetModalityUsingSymbolicName(modality), true); } if (operation == "store-peer")
--- a/OrthancServer/ServerContext.h Fri Jun 26 16:30:51 2015 +0200 +++ b/OrthancServer/ServerContext.h Mon Jun 29 12:42:54 2015 +0200 @@ -98,6 +98,7 @@ const PluginsManager* pluginsManager_; SharedArchive queryRetrieveArchive_; + std::string defaultLocalAet_; public: class DicomCacheLocker : public boost::noncopyable @@ -231,5 +232,10 @@ { return queryRetrieveArchive_; } + + const std::string& GetDefaultLocalApplicationEntityTitle() const + { + return defaultLocalAet_; + } }; }
--- a/Resources/Toolbox.lua Fri Jun 26 16:30:51 2015 +0200 +++ b/Resources/Toolbox.lua Mon Jun 29 12:42:54 2015 +0200 @@ -32,7 +32,7 @@ end -function SendToModality(instanceId, modality) +function SendToModality(instanceId, modality, localAet) if instanceId == nil then error('Cannot send a nonexistent instance') end @@ -40,7 +40,8 @@ table.insert(_job, { Operation = 'store-scu', Instance = instanceId, - Modality = modality + Modality = modality, + LocalAet = localAet }) return instanceId end
--- a/UnitTestsSources/MultiThreadingTests.cpp Fri Jun 26 16:30:51 2015 +0200 +++ b/UnitTestsSources/MultiThreadingTests.cpp Mon Jun 29 12:42:54 2015 +0200 @@ -138,7 +138,8 @@ printf("START\n"); fflush(stdout); { - ReusableDicomUserConnection::Locker lock(c, "STORESCP", "localhost", 2000, ModalityManufacturer_Generic); + 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"); } @@ -147,7 +148,8 @@ printf("**\n"); fflush(stdout); { - ReusableDicomUserConnection::Locker lock(c, "STORESCP", "localhost", 2000, ModalityManufacturer_Generic); + 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"); }