diff OrthancServer/OrthancRestApi/OrthancRestModalities.cpp @ 2573:3372c5255333 jobs

StoreScuJob, Orthanc Explorer for jobs
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 09 May 2018 17:56:14 +0200
parents 878b59270859
children 055d7d4a823f
line wrap: on
line diff
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Mon May 07 21:42:04 2018 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Wed May 09 17:56:14 2018 +0200
@@ -44,6 +44,352 @@
 #include "../QueryRetrieveHandler.h"
 #include "../ServerToolbox.h"
 
+
+
+namespace Orthanc
+{
+  class InstancesIteratorJob : public IJob
+  {
+  private:
+    bool                      started_;
+    std::vector<std::string>  instances_;
+    size_t                    position_;
+
+  public:
+    InstancesIteratorJob() :
+      started_(false),
+      position_(0)
+    {
+    }
+
+    void Reserve(size_t size)
+    {
+      if (started_)
+      {
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+      else
+      {
+        instances_.reserve(size);
+      }
+    }
+
+    size_t GetInstancesCount() const
+    {
+      return instances_.size();
+    }
+    
+    void AddInstance(const std::string& instance)
+    {
+      if (started_)
+      {
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+      else
+      {
+        instances_.push_back(instance);
+      }
+    }
+    
+    virtual void Start()
+    {
+      started_ = true;
+    }
+    
+    virtual float GetProgress()
+    {
+      if (instances_.size() == 0)
+      {
+        return 0;
+      }
+      else
+      {
+        return (static_cast<float>(position_) /
+                static_cast<float>(instances_.size()));
+      }
+    }
+
+    bool IsStarted() const
+    {
+      return started_;
+    }
+
+    bool IsDone() const
+    {
+      return (position_ >= instances_.size());
+    }
+
+    void Next()
+    {
+      if (IsDone())
+      {
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+      else
+      {
+        position_ += 1;
+      }
+    }
+
+    const std::string& GetCurrentInstance() const
+    {
+      if (IsDone())
+      {
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+      else
+      {
+        return instances_[position_];
+      }      
+    }
+  };
+
+
+  class StoreScuJob : public InstancesIteratorJob
+  {
+  private:
+    ServerContext&                      context_;
+    std::string                         localAet_;
+    RemoteModalityParameters            remote_;
+    bool                                permissive_;
+    std::string                         moveOriginatorAet_;
+    uint16_t                            moveOriginatorId_;
+    std::auto_ptr<DicomUserConnection>  connection_;
+    std::set<std::string>               failedInstances_;
+
+    void Open()
+    {
+      if (connection_.get() == NULL)
+      {
+        connection_.reset(new DicomUserConnection);
+        connection_->SetLocalApplicationEntityTitle(localAet_);
+        connection_->SetRemoteModality(remote_);
+        connection_->Open();
+      }
+    }
+    
+  public:
+    StoreScuJob(ServerContext& context) :
+      context_(context),
+      localAet_("ORTHANC"),
+      permissive_(false),
+      moveOriginatorId_(0)  // By default, not a C-MOVE
+    {
+    }
+
+    void AddResource(const std::string& publicId)
+    {
+      typedef std::list<std::string> Instances;
+
+      Instances instances;
+      context_.GetIndex().GetChildInstances(instances, publicId);
+
+      Reserve(GetInstancesCount() + instances.size());
+      
+      for (Instances::const_iterator it = instances.begin();
+           it != instances.end(); ++it)
+      {
+        AddInstance(*it);
+      }
+    }
+
+    const std::string& GetLocalAet() const
+    {
+      return localAet_;
+    }
+
+    void SetLocalAet(const std::string& aet)
+    {
+      if (IsStarted())
+      {
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+      else
+      {
+        localAet_ = aet;
+      }
+    }
+
+    const RemoteModalityParameters& GetRemoteModality() const
+    {
+      return remote_;
+    }
+
+    void SetRemoteModality(const RemoteModalityParameters& remote)
+    {
+      if (IsStarted())
+      {
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+      else
+      {
+        remote_ = remote;
+      }
+    }
+
+    bool IsPermissive() const
+    {
+      return permissive_;
+    }
+
+    void SetPermissive(bool permissive)
+    {
+      if (IsStarted())
+      {
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+      else
+      {
+        permissive_ = permissive;
+      }
+    }
+
+    bool HasMoveOriginator() const
+    {
+      return moveOriginatorId_ != 0;
+    }
+    
+    const std::string& GetMoveOriginatorAet() const
+    {
+      if (HasMoveOriginator())
+      {
+        return moveOriginatorAet_;
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+    }
+    
+    uint16_t GetMoveOriginatorId() const
+    {
+      if (HasMoveOriginator())
+      {
+        return moveOriginatorId_;
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+    }
+
+    void SetMoveOriginator(const std::string& aet,
+                           int id)
+    {
+      if (IsStarted())
+      {
+        throw OrthancException(ErrorCode_BadSequenceOfCalls);
+      }
+      else if (id < 0 || 
+               id >= 65536)
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+      else
+      {
+        moveOriginatorId_ = static_cast<uint16_t>(id);
+        moveOriginatorAet_ = aet;
+      }
+    }
+
+    virtual JobStepResult* ExecuteStep()
+    {
+      if (IsDone())
+      {
+        return new JobStepResult(JobStepCode_Success);
+      }
+
+      Open();
+
+      bool ok = false;
+      
+      try
+      {
+        std::string dicom;
+        context_.ReadDicom(dicom, GetCurrentInstance());
+
+        if (HasMoveOriginator())
+        {
+          connection_->Store(dicom, moveOriginatorAet_, moveOriginatorId_);
+        }
+        else
+        {
+          connection_->Store(dicom);
+        }
+
+        boost::this_thread::sleep(boost::posix_time::milliseconds(300));
+
+        ok = true;
+      }
+      catch (OrthancException& e)
+      {
+      }
+
+      if (!ok)
+      {
+        if (permissive_)
+        {
+          failedInstances_.insert(GetCurrentInstance());
+        }
+        else
+        {
+          return new JobStepResult(JobStepCode_Failure);
+        }
+      }
+
+      Next();
+
+      if (IsDone())
+      {
+        return new JobStepResult(JobStepCode_Success);
+      }
+      else
+      {
+        return new JobStepResult(JobStepCode_Continue);
+      }
+    }
+
+    virtual void ReleaseResources()   // For pausing jobs
+    {
+      connection_.reset(NULL);
+    }
+
+    virtual void GetJobType(std::string& target)
+    {
+      target = "C-Store";
+    }
+
+    virtual void GetPublicContent(Json::Value& value)
+    {
+      value["LocalAet"] = localAet_;
+      value["RemoteAet"] = remote_.GetApplicationEntityTitle();
+
+      if (HasMoveOriginator())
+      {
+        value["MoveOriginatorAET"] = GetMoveOriginatorAet();
+        value["MoveOriginatorID"] = GetMoveOriginatorId();
+      }
+
+      Json::Value v = Json::arrayValue;
+      for (std::set<std::string>::const_iterator it = failedInstances_.begin();
+           it != failedInstances_.end(); ++it)
+      {
+        v.append(*it);
+      }
+
+      value["FailedInstances"] = v;
+    }
+
+    virtual void GetInternalContent(Json::Value& value)
+    {
+      // TODO
+    }
+  };
+}
+
+
+
+
 namespace Orthanc
 {
   /***************************************************************************
@@ -689,6 +1035,7 @@
     bool asynchronous = Toolbox::GetJsonBooleanField(request, "Asynchronous", false);
     std::string moveOriginatorAET = Toolbox::GetJsonStringField(request, "MoveOriginatorAet", context.GetDefaultLocalApplicationEntityTitle());
     int moveOriginatorID = Toolbox::GetJsonIntegerField(request, "MoveOriginatorID", 0 /* By default, not a C-MOVE */);
+    int priority = Toolbox::GetJsonIntegerField(request, "Priority", 0);
 
     if (moveOriginatorID < 0 || 
         moveOriginatorID >= 65536)
@@ -698,6 +1045,44 @@
     
     RemoteModalityParameters p = Configuration::GetModalityUsingSymbolicName(remote);
 
+#if 1
+    std::auto_ptr<StoreScuJob> job(new StoreScuJob(context));
+    job->SetLocalAet(localAet);
+    job->SetRemoteModality(p);
+    job->SetPermissive(permissive);
+
+    if (moveOriginatorID != 0)
+    {
+      job->SetMoveOriginator(moveOriginatorAET, static_cast<uint16_t>(moveOriginatorID));
+    }
+
+    for (std::list<std::string>::const_iterator 
+           it = instances.begin(); it != instances.end(); ++it)
+    {
+      job->AddInstance(*it);
+    }
+
+    if (asynchronous)
+    {
+      // Asynchronous mode: Submit the job, but don't wait for its completion
+      std::string id;
+      context.GetJobsEngine().GetRegistry().Submit(id, job.release(), priority);
+
+      Json::Value v;
+      v["ID"] = id;
+      call.GetOutput().AnswerJson(v);
+    }
+    else if (context.GetJobsEngine().GetRegistry().SubmitAndWait(job.release(), priority))
+    {
+      // Synchronous mode: We have submitted and waited for completion
+      call.GetOutput().AnswerBuffer("{}", "application/json");
+    }
+    else
+    {
+      call.GetOutput().SignalError(HttpStatus_500_InternalServerError);
+    }
+    
+#else
     ServerJob job;
     for (std::list<std::string>::const_iterator 
            it = instances.begin(); it != instances.end(); ++it)
@@ -729,6 +1114,7 @@
     {
       call.GetOutput().SignalError(HttpStatus_500_InternalServerError);
     }
+#endif
   }