changeset 1427:d710ea64f0fd

Custom setting of the local AET during C-Store SCU (both in Lua and in the REST API)
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 29 Jun 2015 12:42:54 +0200
parents 2cf9a12c995a
children 0a355eeeb351
files NEWS OrthancServer/DicomProtocol/DicomUserConnection.cpp OrthancServer/DicomProtocol/DicomUserConnection.h OrthancServer/DicomProtocol/RemoteModalityParameters.cpp OrthancServer/DicomProtocol/RemoteModalityParameters.h OrthancServer/DicomProtocol/ReusableDicomUserConnection.cpp OrthancServer/DicomProtocol/ReusableDicomUserConnection.h OrthancServer/OrthancInitialization.cpp OrthancServer/OrthancMoveRequestHandler.cpp OrthancServer/OrthancRestApi/OrthancRestModalities.cpp OrthancServer/QueryRetrieveHandler.cpp OrthancServer/QueryRetrieveHandler.h OrthancServer/Scheduler/StoreScuCommand.cpp OrthancServer/Scheduler/StoreScuCommand.h OrthancServer/ServerContext.cpp OrthancServer/ServerContext.h Resources/Toolbox.lua UnitTestsSources/MultiThreadingTests.cpp
diffstat 18 files changed, 146 insertions(+), 114 deletions(-) [+]
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");
   }