changeset 70:f73aed014bde wasm

OrthancAsynchronousWebService
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 22 May 2017 21:43:49 +0200
parents 1553b67b24e5
children 30c768873d47
files Framework/Toolbox/IWebService.h Framework/Toolbox/OrthancAsynchronousWebService.cpp Framework/Toolbox/OrthancAsynchronousWebService.h Framework/Toolbox/OrthancSynchronousWebService.cpp Framework/Toolbox/OrthancSynchronousWebService.h Resources/CMake/OrthancStone.cmake Resources/Orthanc/Core/MultiThreading/SharedMessageQueue.cpp Resources/Orthanc/Core/MultiThreading/SharedMessageQueue.h Resources/SyncOrthancFolder.py UnitTestsSources/UnitTestsMain.cpp
diffstat 10 files changed, 666 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Toolbox/IWebService.h	Mon May 22 20:35:11 2017 +0200
+++ b/Framework/Toolbox/IWebService.h	Mon May 22 21:43:49 2017 +0200
@@ -58,5 +58,9 @@
                                      const std::string& uri,
                                      const std::string& body,
                                      Orthanc::IDynamicObject* payload) = 0;
+
+    virtual void Start() = 0;
+    
+    virtual void Stop() = 0;
   };
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Toolbox/OrthancAsynchronousWebService.cpp	Mon May 22 21:43:49 2017 +0200
@@ -0,0 +1,264 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "OrthancAsynchronousWebService.h"
+
+#include "../../Resources/Orthanc/Core/Logging.h"
+#include "../../Resources/Orthanc/Core/OrthancException.h"
+#include "../../Resources/Orthanc/Core/MultiThreading/SharedMessageQueue.h"
+#include "../../Resources/Orthanc/Plugins/Samples/Common/OrthancHttpConnection.h"
+
+namespace OrthancStone
+{
+  class OrthancAsynchronousWebService::PendingRequest : public Orthanc::IDynamicObject
+  {
+  private:
+    bool                                    isPost_;
+    ICallback&                              callback_;
+    std::string                             uri_;
+    std::string                             body_;
+    std::auto_ptr<Orthanc::IDynamicObject>  payload_;
+
+    PendingRequest(bool isPost,
+                   ICallback& callback,
+                   const std::string& uri,
+                   const std::string& body,
+                   Orthanc::IDynamicObject* payload) :
+      isPost_(isPost),
+      callback_(callback),
+      uri_(uri),
+      body_(body),
+      payload_(payload)
+    {
+    }
+    
+  public:
+    static PendingRequest* CreateGetRequest(ICallback& callback,
+                                            const std::string& uri,
+                                            Orthanc::IDynamicObject* payload)
+    {
+      return new PendingRequest(false, callback, uri, "", payload);
+    }
+
+    static PendingRequest* CreatePostRequest(ICallback& callback,
+                                             const std::string& uri,
+                                             const std::string& body,
+                                             Orthanc::IDynamicObject* payload)
+    {
+      return new PendingRequest(true, callback, uri, body, payload);
+    }
+
+    void Execute(OrthancPlugins::OrthancHttpConnection& connection)
+    {
+      if (payload_.get() == NULL)
+      {
+        // Execute() has already been invoked
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);        
+      }
+
+      std::string answer;
+      
+      try
+      {
+        if (isPost_)
+        {
+          connection.RestApiPost(answer, uri_, body_);
+        }
+        else
+        {
+          connection.RestApiGet(answer, uri_);
+        }
+      }
+      catch (Orthanc::OrthancException&)
+      {
+        callback_.NotifyError(uri_, payload_.release());
+      }
+
+      callback_.NotifySuccess(uri_, answer.c_str(), answer.size(), payload_.release());
+    }
+  };
+    
+  class OrthancAsynchronousWebService::PImpl : public boost::noncopyable
+  {
+  private:
+    enum State
+    {
+      State_Init,
+      State_Started,
+      State_Stopped
+    };
+    
+    boost::mutex                   mutex_;
+    State                          state_;
+    Orthanc::WebServiceParameters  orthanc_;
+    std::vector<boost::thread>     threads_;
+    Orthanc::SharedMessageQueue    queue_;
+
+    static void Worker(PImpl* that)
+    {
+      OrthancPlugins::OrthancHttpConnection connection(that->orthanc_);
+      
+      for (;;)
+      {
+        State state;
+        
+        {
+          boost::mutex::scoped_lock lock(that->mutex_);
+          state = that->state_;
+        }
+
+        printf("."); fflush(stdout);
+        if (state == State_Stopped)
+        {
+          break;
+        }
+
+        std::auto_ptr<Orthanc::IDynamicObject> request(that->queue_.Dequeue(100));
+        if (request.get() != NULL)
+        {
+          dynamic_cast<PendingRequest&>(*request).Execute(connection);          
+        }
+      }
+    }
+    
+  public:
+    PImpl(const Orthanc::WebServiceParameters& orthanc,
+          unsigned int threadCount) :
+      state_(State_Init),
+      orthanc_(orthanc),
+      threads_(threadCount)
+    {
+    }
+
+    ~PImpl()
+    {
+      if (state_ == State_Started)
+      {
+        LOG(ERROR) << "You should have manually called OrthancAsynchronousWebService::Stop()";
+        Stop();
+      }
+    }
+
+    void Schedule(PendingRequest* request)
+    {
+      std::auto_ptr<PendingRequest> protection(request);
+
+      if (request == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+      
+      boost::mutex::scoped_lock lock(mutex_);
+
+      switch (state_)
+      {
+        case State_Init:
+          LOG(ERROR) << "You must call OrthancAsynchronousWebService::Start()";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+
+        case State_Started:
+          queue_.Enqueue(protection.release());
+          break;
+
+        case State_Stopped:
+          LOG(ERROR) << "Cannot schedule a Web request after having "
+                     << "called OrthancAsynchronousWebService::Stop()";
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+      
+    }
+
+    void Start()
+    {
+      boost::mutex::scoped_lock lock(mutex_);
+
+      if (state_ != State_Init)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+      
+      for (size_t i = 0; i < threads_.size(); i++)
+      {
+        threads_[i] = boost::thread(Worker, this);
+      }
+
+      state_ = State_Started;
+    }
+
+    void Stop()
+    {
+      {
+        boost::mutex::scoped_lock lock(mutex_);
+
+        if (state_ != State_Started)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+        }
+
+        state_ = State_Stopped;
+      }
+      
+      for (size_t i = 0; i < threads_.size(); i++)
+      {
+        if (threads_[i].joinable())
+        {
+          threads_[i].join();
+        }
+      }
+    }
+  };
+
+  OrthancAsynchronousWebService::OrthancAsynchronousWebService(
+    const Orthanc::WebServiceParameters& parameters,
+    unsigned int threadCount) :
+    pimpl_(new PImpl(parameters, threadCount))
+  {
+  }
+
+  void  OrthancAsynchronousWebService::ScheduleGetRequest(ICallback& callback,
+                                                          const std::string& uri,
+                                                          Orthanc::IDynamicObject* payload)
+  {
+    pimpl_->Schedule(PendingRequest::CreateGetRequest(callback, uri, payload));
+  }
+  
+
+  void  OrthancAsynchronousWebService::SchedulePostRequest(ICallback& callback,
+                                                           const std::string& uri,
+                                                           const std::string& body,
+                                                           Orthanc::IDynamicObject* payload)
+  {
+    pimpl_->Schedule(PendingRequest::CreatePostRequest(callback, uri, body, payload));
+  }
+
+  void  OrthancAsynchronousWebService::Start()
+  {
+    pimpl_->Start();
+  }
+
+  void  OrthancAsynchronousWebService::Stop()
+  {
+    pimpl_->Stop();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Toolbox/OrthancAsynchronousWebService.h	Mon May 22 21:43:49 2017 +0200
@@ -0,0 +1,57 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IWebService.h"
+#include "../../Resources/Orthanc/Plugins/Samples/Common/IOrthancConnection.h"
+#include "../../Resources/Orthanc/Core/WebServiceParameters.h"
+
+#include <boost/shared_ptr.hpp>
+
+namespace OrthancStone
+{
+  class OrthancAsynchronousWebService : public IWebService
+  {
+  private:
+    class PendingRequest;
+    class PImpl;
+    
+    boost::shared_ptr<PImpl>  pimpl_;
+    
+  public:
+    OrthancAsynchronousWebService(const Orthanc::WebServiceParameters& parameters,
+                                  unsigned int threadCount);
+
+    virtual void ScheduleGetRequest(ICallback& callback,
+                                    const std::string& uri,
+                                    Orthanc::IDynamicObject* payload);
+
+    virtual void SchedulePostRequest(ICallback& callback,
+                                     const std::string& uri,
+                                     const std::string& body,
+                                     Orthanc::IDynamicObject* payload);
+
+    virtual void Start();
+
+    virtual void Stop();
+  };
+}
--- a/Framework/Toolbox/OrthancSynchronousWebService.cpp	Mon May 22 20:35:11 2017 +0200
+++ b/Framework/Toolbox/OrthancSynchronousWebService.cpp	Mon May 22 21:43:49 2017 +0200
@@ -41,8 +41,8 @@
   }    
 
   void OrthancSynchronousWebService::ScheduleGetRequest(ICallback& callback,
-                                             const std::string& uri,
-                                             Orthanc::IDynamicObject* payload)
+                                                        const std::string& uri,
+                                                        Orthanc::IDynamicObject* payload)
   {
     std::auto_ptr<Orthanc::IDynamicObject> tmp(payload);
 
@@ -59,9 +59,9 @@
   }
 
   void OrthancSynchronousWebService::SchedulePostRequest(ICallback& callback,
-                                              const std::string& uri,
-                                              const std::string& body,
-                                              Orthanc::IDynamicObject* payload)
+                                                         const std::string& uri,
+                                                         const std::string& body,
+                                                         Orthanc::IDynamicObject* payload)
   {
     std::auto_ptr<Orthanc::IDynamicObject> tmp(payload);
 
--- a/Framework/Toolbox/OrthancSynchronousWebService.h	Mon May 22 20:35:11 2017 +0200
+++ b/Framework/Toolbox/OrthancSynchronousWebService.h	Mon May 22 21:43:49 2017 +0200
@@ -27,6 +27,8 @@
 
 #include <memory>
 
+// TODO REMOVE THIS
+
 namespace OrthancStone
 {
   class OrthancSynchronousWebService : public IWebService
@@ -52,5 +54,13 @@
                                      const std::string& uri,
                                      const std::string& body,
                                      Orthanc::IDynamicObject* payload);
+
+    virtual void Start()
+    {
+    }
+
+    virtual void Stop()
+    {
+    }
   };
 }
--- a/Resources/CMake/OrthancStone.cmake	Mon May 22 20:35:11 2017 +0200
+++ b/Resources/CMake/OrthancStone.cmake	Mon May 22 21:43:49 2017 +0200
@@ -200,6 +200,7 @@
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/DownloadStack.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/GeometryToolbox.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/MessagingToolbox.cpp
+  ${ORTHANC_STONE_DIR}/Framework/Toolbox/OrthancAsynchronousWebService.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/OrthancSeriesLoader.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/OrthancSynchronousWebService.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/ParallelSlices.cpp
@@ -237,6 +238,7 @@
   ${ORTHANC_ROOT}/Core/Images/JpegReader.cpp
   ${ORTHANC_ROOT}/Core/Images/PngReader.cpp
   ${ORTHANC_ROOT}/Core/Logging.cpp
+  ${ORTHANC_ROOT}/Core/MultiThreading/SharedMessageQueue.cpp
   ${ORTHANC_ROOT}/Core/Toolbox.cpp
   ${ORTHANC_ROOT}/Core/WebServiceParameters.cpp
   ${ORTHANC_ROOT}/Resources/ThirdParty/base64/base64.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Core/MultiThreading/SharedMessageQueue.cpp	Mon May 22 21:43:49 2017 +0200
@@ -0,0 +1,209 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "SharedMessageQueue.h"
+
+
+
+/**
+ * FIFO (queue):
+ * 
+ *            back                         front
+ *            +--+--+--+--+--+--+--+--+--+--+--+
+ * Enqueue -> |  |  |  |  |  |  |  |  |  |  |  |
+ *            |  |  |  |  |  |  |  |  |  |  |  | -> Dequeue
+ *            +--+--+--+--+--+--+--+--+--+--+--+
+ *                                            ^
+ *                                            |
+ *                                      Make room here
+ *
+ *
+ * LIFO (stack):
+ * 
+ *            back                         front
+ *            +--+--+--+--+--+--+--+--+--+--+--+
+ *            |  |  |  |  |  |  |  |  |  |  |  | <- Enqueue
+ *            |  |  |  |  |  |  |  |  |  |  |  | -> Dequeue
+ *            +--+--+--+--+--+--+--+--+--+--+--+
+ *              ^
+ *              |
+ *        Make room here
+ **/
+
+
+namespace Orthanc
+{
+  SharedMessageQueue::SharedMessageQueue(unsigned int maxSize) :
+    isFifo_(true),
+    maxSize_(maxSize)
+  {
+  }
+
+
+  SharedMessageQueue::~SharedMessageQueue()
+  {
+    for (Queue::iterator it = queue_.begin(); it != queue_.end(); ++it)
+    {
+      delete *it;
+    }
+  }
+
+
+  void SharedMessageQueue::Enqueue(IDynamicObject* message)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+
+    if (maxSize_ != 0 && queue_.size() > maxSize_)
+    {
+      if (isFifo_)
+      {
+        // Too many elements in the queue: Make room
+        delete queue_.front();
+        queue_.pop_front();
+      }
+      else
+      {
+        // Too many elements in the stack: Make room
+        delete queue_.back();
+        queue_.pop_back();
+      }
+    }
+
+    if (isFifo_)
+    {
+      // Queue policy (FIFO)
+      queue_.push_back(message);
+    }
+    else
+    {
+      // Stack policy (LIFO)
+      queue_.push_front(message);
+    }
+
+    elementAvailable_.notify_one();
+  }
+
+
+  IDynamicObject* SharedMessageQueue::Dequeue(int32_t millisecondsTimeout)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+
+    // Wait for a message to arrive in the FIFO queue
+    while (queue_.empty())
+    {
+      if (millisecondsTimeout == 0)
+      {
+        elementAvailable_.wait(lock);
+      }
+      else
+      {
+        bool success = elementAvailable_.timed_wait
+          (lock, boost::posix_time::milliseconds(millisecondsTimeout));
+        if (!success)
+        {
+          return NULL;
+        }
+      }
+    }
+
+    std::auto_ptr<IDynamicObject> message(queue_.front());
+    queue_.pop_front();
+
+    if (queue_.empty())
+    {
+      emptied_.notify_all();
+    }
+
+    return message.release();
+  }
+
+
+
+  bool SharedMessageQueue::WaitEmpty(int32_t millisecondsTimeout)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+    
+    // Wait for the queue to become empty
+    while (!queue_.empty())
+    {
+      if (millisecondsTimeout == 0)
+      {
+        emptied_.wait(lock);
+      }
+      else
+      {
+        if (!emptied_.timed_wait
+            (lock, boost::posix_time::milliseconds(millisecondsTimeout)))
+        {
+          return false;
+        }
+      }
+    }
+
+    return true;
+  }
+
+
+  void SharedMessageQueue::SetFifoPolicy()
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+    isFifo_ = true;
+  }
+
+  void SharedMessageQueue::SetLifoPolicy()
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+    isFifo_ = false;
+  }
+
+  void SharedMessageQueue::Clear()
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+
+    if (queue_.empty())
+    {
+      return;
+    }
+    else
+    {
+      while (!queue_.empty())
+      {
+        std::auto_ptr<IDynamicObject> message(queue_.front());
+        queue_.pop_front();
+      }
+
+      emptied_.notify_all();
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Core/MultiThreading/SharedMessageQueue.h	Mon May 22 21:43:49 2017 +0200
@@ -0,0 +1,85 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../IDynamicObject.h"
+
+#include <stdint.h>
+#include <list>
+#include <boost/thread.hpp>
+
+namespace Orthanc
+{
+  class SharedMessageQueue : public boost::noncopyable
+  {
+  private:
+    typedef std::list<IDynamicObject*>  Queue;
+
+    bool isFifo_;
+    unsigned int maxSize_;
+    Queue queue_;
+    boost::mutex mutex_;
+    boost::condition_variable elementAvailable_;
+    boost::condition_variable emptied_;
+
+  public:
+    explicit SharedMessageQueue(unsigned int maxSize = 0);
+    
+    ~SharedMessageQueue();
+
+    // This transfers the ownership of the message
+    void Enqueue(IDynamicObject* message);
+
+    // The caller is responsible to delete the dequeud message!
+    IDynamicObject* Dequeue(int32_t millisecondsTimeout);
+
+    bool WaitEmpty(int32_t millisecondsTimeout);
+
+    bool IsFifoPolicy() const
+    {
+      return isFifo_;
+    }
+
+    bool IsLifoPolicy() const
+    {
+      return !isFifo_;
+    }
+
+    void SetFifoPolicy();
+
+    void SetLifoPolicy();
+
+    void Clear();
+  };
+}
--- a/Resources/SyncOrthancFolder.py	Mon May 22 20:35:11 2017 +0200
+++ b/Resources/SyncOrthancFolder.py	Mon May 22 21:43:49 2017 +0200
@@ -43,6 +43,8 @@
     'Core/Images/PngReader.h',
     'Core/Logging.cpp',
     'Core/Logging.h',
+    'Core/MultiThreading/SharedMessageQueue.cpp',
+    'Core/MultiThreading/SharedMessageQueue.h',
     'Core/OrthancException.h',
     'Core/PrecompiledHeaders.h',
     'Core/SystemToolbox.cpp',
--- a/UnitTestsSources/UnitTestsMain.cpp	Mon May 22 20:35:11 2017 +0200
+++ b/UnitTestsSources/UnitTestsMain.cpp	Mon May 22 21:43:49 2017 +0200
@@ -21,6 +21,7 @@
 
 #include "gtest/gtest.h"
 
+#include "../Framework/Toolbox/OrthancAsynchronousWebService.h"
 #include "../Resources/Orthanc/Core/Logging.h"
 #include "../Framework/Toolbox/OrthancSynchronousWebService.h"
 #include "../Framework/Layers/OrthancFrameLayerSource.h"
@@ -32,6 +33,8 @@
 #include "../Framework/Toolbox/DicomFrameConverter.h"
 
 #include <boost/lexical_cast.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/thread/thread.hpp> 
 
 namespace OrthancStone
 {
@@ -399,16 +402,16 @@
       {
       }
 
-      virtual void NotifyGeometryReady(const OrthancSliceLoader& loader) = 0;
+      virtual void NotifyGeometryReady(OrthancSliceLoader& loader) = 0;
 
-      virtual void NotifyGeometryError(const OrthancSliceLoader& loader) = 0;
+      virtual void NotifyGeometryError(OrthancSliceLoader& loader) = 0;
 
-      virtual void NotifySliceImageReady(const OrthancSliceLoader& loader,
+      virtual void NotifySliceImageReady(OrthancSliceLoader& loader,
                                          unsigned int sliceIndex,
                                          const Slice& slice,
                                          Orthanc::ImageAccessor* image) = 0;
 
-      virtual void NotifySliceImageError(const OrthancSliceLoader& loader,
+      virtual void NotifySliceImageError(OrthancSliceLoader& loader,
                                          unsigned int sliceIndex,
                                          const Slice& slice) = 0;
     };
@@ -523,6 +526,8 @@
         }
       }
 
+      state_ = State_GeometryReady;
+
       if (ok)
       {
         LOG(INFO) << "Loaded a series with " << slices_.GetSliceCount() << " slice(s)";
@@ -662,7 +667,6 @@
       {
         case Mode_SeriesGeometry:
           ParseSeriesGeometry(answer, answerSize);
-          state_ = State_GeometryReady;
           break;
 
         case Mode_LoadImage:
@@ -701,26 +705,31 @@
   class Tata : public OrthancSliceLoader::ICallback
   {
   public:
-    virtual void NotifyGeometryReady(const OrthancSliceLoader& loader)
+    virtual void NotifyGeometryReady(OrthancSliceLoader& loader)
     {
-      printf("Done\n");
+      printf(">> %d\n", loader.GetSliceCount());
+
+      for (size_t i = 0; i < loader.GetSliceCount(); i++)
+      {
+        loader.ScheduleLoadSliceImage(i);
+      }
     }
 
-    virtual void NotifyGeometryError(const OrthancSliceLoader& loader)
+    virtual void NotifyGeometryError(OrthancSliceLoader& loader)
     {
       printf("Error\n");
     }
 
-    virtual void NotifySliceImageReady(const OrthancSliceLoader& loader,
+    virtual void NotifySliceImageReady(OrthancSliceLoader& loader,
                                        unsigned int sliceIndex,
                                        const Slice& slice,
                                        Orthanc::ImageAccessor* image)
     {
       std::auto_ptr<Orthanc::ImageAccessor> tmp(image);
-      printf("Slice OK\n");
+      printf("Slice OK %dx%d\n", tmp->GetWidth(), tmp->GetHeight());
     }
 
-    virtual void NotifySliceImageError(const OrthancSliceLoader& loader,
+    virtual void NotifySliceImageError(OrthancSliceLoader& loader,
                                        unsigned int sliceIndex,
                                        const Slice& slice)
     {
@@ -733,15 +742,20 @@
 TEST(Toto, Tutu)
 {
   Orthanc::WebServiceParameters web;
-  OrthancStone::OrthancSynchronousWebService orthanc(web);
+  OrthancStone::OrthancAsynchronousWebService orthanc(web, 4);
+  orthanc.Start();
 
   OrthancStone::Tata tata;
   OrthancStone::OrthancSliceLoader loader(tata, orthanc);
   //loader.ScheduleLoadSeries("c1c4cb95-05e3bd11-8da9f5bb-87278f71-0b2b43f5");
   loader.ScheduleLoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5");
 
-  printf(">> %d\n", loader.GetSliceCount());
-  loader.ScheduleLoadSliceImage(31);
+  /*printf(">> %d\n", loader.GetSliceCount());
+    loader.ScheduleLoadSliceImage(31);*/
+
+  boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
+
+  orthanc.Stop();
 }