changeset 485:bdbde1fbfab3

send resources through HTTP
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 16 Jul 2013 13:48:33 +0200
parents b8ace6fc1d1f
children 49aa17c03cd3
files OrthancExplorer/explorer.html OrthancExplorer/explorer.js OrthancServer/OrthancInitialization.cpp OrthancServer/OrthancRestApi.cpp Resources/Configuration.json
diffstat 5 files changed, 188 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancExplorer/explorer.html	Tue Jul 16 12:51:27 2013 +0200
+++ b/OrthancExplorer/explorer.html	Tue Jul 16 13:48:33 2013 +0200
@@ -253,7 +253,12 @@
       </div>
     </div>
 
-    <div id="loading" style="display:none;" class="ui-body-c">
+    <div id="peer-store" style="display:none;" class="ui-body-c">
+      <p align="center"><b>Sending to Orthanc peer...</b></p>
+      <p><img src="libs/images/ajax-loader2.gif" alt="" /></p>
+    </div>
+
+    <div id="dicom-store" style="display:none;" class="ui-body-c">
       <p align="center"><b>Sending to DICOM modality...</b></p>
       <p><img src="libs/images/ajax-loader2.gif" alt="" /></p>
     </div>
--- a/OrthancExplorer/explorer.js	Tue Jul 16 12:51:27 2013 +0200
+++ b/OrthancExplorer/explorer.js	Tue Jul 16 13:48:33 2013 +0200
@@ -783,6 +783,13 @@
 
 function ChooseDicomModality(callback)
 {
+  var clickedModality = '';
+  var clickedPeer = '';
+  var items = $('<ul>')
+    .attr('data-divider-theme', 'd')
+    .attr('data-role', 'listview');
+
+  // Retrieve the list of the known DICOM modalities
   $.ajax({
     url: '../modalities',
     type: 'GET',
@@ -790,43 +797,66 @@
     async: false,
     cache: false,
     success: function(modalities) {
-      var clickedModality = '';
-      var items = $('<ul>')
-        .attr('data-divider-theme', 'd')
-        .attr('data-role', 'listview');
-
-      items.append('<li data-role="list-divider">DICOM modalities</li>');
+      if (modalities.length > 0)
+      {
+        items.append('<li data-role="list-divider">DICOM modalities</li>');
 
-      for (var i = 0; i < modalities.length; i++) {
-        var modality = modalities[i];
-        var item = $('<li>')
-          .html('<a href="#" rel="close">' + modality + '</a>')
-          .attr('modality', modality)
-          .click(function() { 
-            clickedModality = $(this).attr('modality');
-          });
-        items.append(item);
+        for (var i = 0; i < modalities.length; i++) {
+          var name = modalities[i];
+          var item = $('<li>')
+            .html('<a href="#" rel="close">' + name + '</a>')
+            .attr('name', name)
+            .click(function() { 
+              clickedModality = $(this).attr('name');
+            });
+          items.append(item);
+        }
       }
 
-      items.append('<li data-role="list-divider">Orthanc peers</li>');
-
+      // Retrieve the list of the known Orthanc peers
+      $.ajax({
+        url: '../peers',
+        type: 'GET',
+        dataType: 'json',
+        async: false,
+        cache: false,
+        success: function(peers) {
+          if (peers.length > 0)
+          {
+            items.append('<li data-role="list-divider">Orthanc peers</li>');
 
-      $('#dialog').simpledialog2({
-        mode: 'blank',
-        animate: false,
-        headerText: 'Choose target',
-        headerClose: true,
-        width: '100%',
-        blankContent: items,
-        callbackClose: function() {
-          var timer;
-          function WaitForDialogToClose() {
-            if (!$('#dialog').is(':visible')) {
-              clearInterval(timer);
-              callback(clickedModality);
+            for (var i = 0; i < peers.length; i++) {
+              var name = peers[i];
+              var item = $('<li>')
+                .html('<a href="#" rel="close">' + name + '</a>')
+                .attr('name', name)
+                .click(function() { 
+                  clickedPeer = $(this).attr('name');
+                });
+              items.append(item);
             }
           }
-          timer = setInterval(WaitForDialogToClose, 100);
+
+          // Launch the dialog
+          $('#dialog').simpledialog2({
+            mode: 'blank',
+            animate: false,
+            headerText: 'Choose target',
+            headerClose: true,
+            forceInput: false,
+            width: '100%',
+            blankContent: items,
+            callbackClose: function() {
+              var timer;
+              function WaitForDialogToClose() {
+                if (!$('#dialog').is(':visible')) {
+                  clearInterval(timer);
+                  callback(clickedModality, clickedPeer);
+                }
+              }
+              timer = setInterval(WaitForDialogToClose, 100);
+            }
+          });
         }
       });
     }
@@ -835,16 +865,31 @@
 
 
 $('#instance-store,#series-store,#study-store,#patient-store').live('click', function(e) {
-  ChooseDicomModality(function(modality) {
-    if (modality != '') {
+  ChooseDicomModality(function(modality, peer) {
+    var url;
+    var loading;
+
+    if (modality != '')
+    {
+      url = '../modalities/' + modality + '/store';
+      loading = '#dicom-store';
+    }
+
+    if (peer != '')
+    {
+      url = '../peers/' + peer + '/store';
+      loading = '#peer-store';
+    }
+
+    if (url != '') {
       $.ajax({
-        url: '../modalities/' + modality + '/store',
+        url: url,
         type: 'POST',
         dataType: 'text',
         data: $.mobile.pageData.uuid,
         async: true,  // Necessary to block UI
         beforeSend: function() {
-          $.blockUI({ message: $('#loading') });
+          $.blockUI({ message: $(loading) });
         },
         complete: function(s) {
           $.unblockUI();
@@ -852,10 +897,9 @@
         success: function(s) {
         },
         error: function() {
-          alert('Error during C-Store');
+          alert('Error during store');
         }
-      });
-      
+      });      
     }
   });
 });
--- a/OrthancServer/OrthancInitialization.cpp	Tue Jul 16 12:51:27 2013 +0200
+++ b/OrthancServer/OrthancInitialization.cpp	Tue Jul 16 13:48:33 2013 +0200
@@ -282,12 +282,30 @@
     try
     {
       url = modalities[name].get(0u, "").asString();
-      username = modalities[name].get(1u, "").asString();
-      password = modalities[name].get(2u, "").asString();
+
+      if (modalities[name].size() == 1)
+      {
+        username = "";
+        password = "";
+      }
+      else if (modalities[name].size() == 3)
+      {
+        username = modalities[name].get(1u, "").asString();
+        password = modalities[name].get(2u, "").asString();
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
     }
     catch (...)
     {
-      throw OrthancException("Badly formatted Orthanc peer");
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    if (url.size() != 0 && url[url.size() - 1] != '/')
+    {
+      url += '/';
     }
   }
 
--- a/OrthancServer/OrthancRestApi.cpp	Tue Jul 16 12:51:27 2013 +0200
+++ b/OrthancServer/OrthancRestApi.cpp	Tue Jul 16 13:48:33 2013 +0200
@@ -32,9 +32,10 @@
 
 #include "OrthancRestApi.h"
 
+#include "../Core/Compression/HierarchicalZipWriter.h"
+#include "../Core/HttpClient.h"
 #include "../Core/HttpServer/FilesystemHttpSender.h"
 #include "../Core/Uuid.h"
-#include "../Core/Compression/HierarchicalZipWriter.h"
 #include "DicomProtocol/DicomUserConnection.h"
 #include "FromDcmtkBridge.h"
 #include "OrthancInitialization.h"
@@ -248,11 +249,12 @@
   }
 
 
-  static void DicomStore(RestApi::PostCall& call)
+  static bool GetInstancesToExport(std::list<std::string>& instances,
+                                   const std::string& remote,
+                                   RestApi::PostCall& call)
   {
     RETRIEVE_CONTEXT(call);
 
-    std::string remote = call.GetUriComponent("id", "");
     std::string stripped = Toolbox::StripSpaces(call.GetPostBody());
 
     Json::Value request;
@@ -264,13 +266,11 @@
     else if (!call.ParseJsonRequest(request))
     {
       // Bad JSON request
-      return;
+      return false;
     }
 
-    std::list<std::string> instances;
     if (request.isString())
     {
-      LOG(INFO) << "Sending resource " << request.asString() << " to modality " << remote;
       context.GetIndex().LogExportedResource(request.asString(), remote);
       context.GetIndex().GetChildInstances(instances, request.asString());
     }
@@ -280,16 +280,15 @@
       {
         if (!request[i].isString())
         {
-          return;
+          return false;
         }
 
         std::string stripped = Toolbox::StripSpaces(request[i].asString());
         if (!Toolbox::IsSHA1(stripped))
         {
-          return;
+          return false;
         }
 
-        LOG(INFO) << "Sending resource " << stripped << " to modality " << remote;
         context.GetIndex().LogExportedResource(stripped, remote);
        
         std::list<std::string> tmp;
@@ -301,6 +300,22 @@
     else
     {
       // Neither a string, nor a list of strings. Bad request.
+      return false;
+    }
+
+    return true;
+  }
+
+
+  static void DicomStore(RestApi::PostCall& call)
+  {
+    RETRIEVE_CONTEXT(call);
+
+    std::string remote = call.GetUriComponent("id", "");
+
+    std::list<std::string> instances;
+    if (!GetInstancesToExport(instances, remote, call))
+    {
       return;
     }
 
@@ -310,6 +325,8 @@
     for (std::list<std::string>::const_iterator 
            it = instances.begin(); it != instances.end(); it++)
     {
+      LOG(INFO) << "Sending resource " << *it << " to modality \"" << remote << "\"";
+
       std::string dicom;
       context.ReadFile(dicom, *it, FileContentType_Dicom);
       connection.Store(dicom);
@@ -1649,6 +1666,51 @@
     }
   }
 
+  static void PeerStore(RestApi::PostCall& call)
+  {
+    RETRIEVE_CONTEXT(call);
+
+    std::string remote = call.GetUriComponent("id", "");
+
+    std::list<std::string> instances;
+    if (!GetInstancesToExport(instances, remote, call))
+    {
+      return;
+    }
+
+    std::string url, username, password;
+    GetOrthancPeer(remote, url, username, password);
+
+    // Configure the HTTP client
+    HttpClient client;
+    if (username.size() != 0 && password.size() != 0)
+    {
+      client.SetCredentials(username.c_str(), password.c_str());
+    }
+
+    client.SetUrl(url + "instances");
+    client.SetMethod(HttpMethod_Post);
+
+    // Loop over the instances that are to be sent
+    for (std::list<std::string>::const_iterator 
+           it = instances.begin(); it != instances.end(); it++)
+    {
+      LOG(INFO) << "Sending resource " << *it << " to peer \"" << remote << "\"";
+
+      context.ReadFile(client.AccessPostData(), *it, FileContentType_Dicom);
+
+      std::string answer;
+      if (!client.Apply(answer))
+      {
+        LOG(ERROR) << "Unable to send resource " << *it << " to peer \"" << remote << "\"";
+        return;
+      }
+    }
+
+    call.GetOutput().AnswerBuffer("{}", "application/json");
+  }
+
+
 
 
 
@@ -1737,6 +1799,7 @@
 
     Register("/peers", ListPeers);
     Register("/peers/{id}", ListPeerOperations);
+    Register("/peers/{id}/store", PeerStore);
 
     Register("/instances/{id}/modify", ModifyInstance);
     Register("/series/{id}/modify", ModifySeriesInplace);
--- a/Resources/Configuration.json	Tue Jul 16 12:51:27 2013 +0200
+++ b/Resources/Configuration.json	Tue Jul 16 13:48:33 2013 +0200
@@ -107,6 +107,12 @@
 
   // The list of the known Orthanc peers
   "OrthancPeers" : {
-    // "peer" : [ "http://localhost:8043/", "alice", "alicePassword" ]
+    /**
+     * Each line gives the base URL of an Orthanc peer, possibly
+     * followed by the username/password pair (if the password
+     * protection is enabled on the peer).
+     **/
+    // "peer"  : [ "http://localhost:8043/", "alice", "alicePassword" ]
+    // "peer2" : [ "http://localhost:8044/" ]
   }
 }