Mercurial > hg > orthanc
changeset 1007:871c49c9b11d lua-scripting
lua routing is working
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 08 Jul 2014 17:35:00 +0200 |
parents | 649d47854314 |
children | 187ed107a59f |
files | CMakeLists.txt OrthancServer/OrthancInitialization.cpp OrthancServer/OrthancRestApi/OrthancRestModalities.cpp OrthancServer/Scheduler/StorePeerCommand.cpp OrthancServer/Scheduler/StorePeerCommand.h OrthancServer/Scheduler/StoreScuCommand.cpp OrthancServer/ServerContext.cpp Resources/Samples/Lua/Autorouting.lua |
diffstat | 8 files changed, 320 insertions(+), 68 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Tue Jul 08 15:11:00 2014 +0200 +++ b/CMakeLists.txt Tue Jul 08 17:35:00 2014 +0200 @@ -160,6 +160,7 @@ OrthancServer/Scheduler/ServerJob.cpp OrthancServer/Scheduler/ServerScheduler.cpp OrthancServer/Scheduler/StoreScuCommand.cpp + OrthancServer/Scheduler/StorePeerCommand.cpp )
--- a/OrthancServer/OrthancInitialization.cpp Tue Jul 08 15:11:00 2014 +0200 +++ b/OrthancServer/OrthancInitialization.cpp Tue Jul 08 17:35:00 2014 +0200 @@ -288,7 +288,8 @@ if (modalities.type() != Json::objectValue || !modalities.isMember(name)) { - throw OrthancException(ErrorCode_BadFileFormat); + LOG(ERROR) << "No modality with symbolic name: " << name; + throw OrthancException(ErrorCode_InexistentItem); } try @@ -321,7 +322,8 @@ if (modalities.type() != Json::objectValue || !modalities.isMember(name)) { - throw OrthancException(ErrorCode_BadFileFormat); + LOG(ERROR) << "No peer with symbolic name: " << name; + throw OrthancException(ErrorCode_InexistentItem); } peer.FromJson(modalities[name]);
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Tue Jul 08 15:11:00 2014 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Tue Jul 08 17:35:00 2014 +0200 @@ -38,6 +38,7 @@ #include "../FromDcmtkBridge.h" #include "../Scheduler/ServerJob.h" #include "../Scheduler/StoreScuCommand.h" +#include "../Scheduler/StorePeerCommand.h" #include <glog/logging.h> @@ -331,7 +332,7 @@ job.AddCommand(new StoreScuCommand(context, p)).AddInput(*it); } - job.SetDescription("Store-SCU from HTTP to modality \"" + remote + "\""); + job.SetDescription("HTTP request: Store-SCU to peer \"" + remote + "\""); context.GetScheduler().SubmitAndWait(job); call.GetOutput().AnswerBuffer("{}", "application/json"); @@ -390,33 +391,15 @@ OrthancPeerParameters peer; Configuration::GetOrthancPeer(peer, remote); - // Configure the HTTP client - HttpClient client; - if (peer.GetUsername().size() != 0 && - peer.GetPassword().size() != 0) - { - client.SetCredentials(peer.GetUsername().c_str(), - peer.GetPassword().c_str()); - } - - client.SetUrl(peer.GetUrl() + "instances"); - client.SetMethod(HttpMethod_Post); - - // Loop over the instances that are to be sent + ServerJob job; 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); + job.AddCommand(new StorePeerCommand(context, peer)).AddInput(*it); + } - std::string answer; - if (!client.Apply(answer)) - { - LOG(ERROR) << "Unable to send resource " << *it << " to peer \"" << remote << "\""; - return; - } - } + job.SetDescription("HTTP request: POST to peer \"" + remote + "\""); + context.GetScheduler().SubmitAndWait(job); call.GetOutput().AnswerBuffer("{}", "application/json"); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Scheduler/StorePeerCommand.cpp Tue Jul 08 17:35:00 2014 +0200 @@ -0,0 +1,83 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, + * 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 "StorePeerCommand.h" + +#include "../../Core/HttpClient.h" + +#include <glog/logging.h> + +namespace Orthanc +{ + StorePeerCommand::StorePeerCommand(ServerContext& context, + const OrthancPeerParameters& peer) : + context_(context), + peer_(peer) + { + } + + bool StorePeerCommand::Apply(ListOfStrings& outputs, + const ListOfStrings& inputs) + { + // Configure the HTTP client + HttpClient client; + if (peer_.GetUsername().size() != 0 && + peer_.GetPassword().size() != 0) + { + client.SetCredentials(peer_.GetUsername().c_str(), + peer_.GetPassword().c_str()); + } + + client.SetUrl(peer_.GetUrl() + "instances"); + client.SetMethod(HttpMethod_Post); + + for (ListOfStrings::const_iterator + it = inputs.begin(); it != inputs.end(); ++it) + { + LOG(INFO) << "Sending resource " << *it << " to peer \"" + << peer_.GetUrl() << "\""; + + context_.ReadFile(client.AccessPostData(), *it, FileContentType_Dicom); + + std::string answer; + if (!client.Apply(answer)) + { + LOG(ERROR) << "Unable to send resource " << *it << " to peer \"" << peer_.GetUrl() << "\""; + throw OrthancException(ErrorCode_NetworkProtocol); + } + + outputs.push_back(*it); + } + + return true; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Scheduler/StorePeerCommand.h Tue Jul 08 17:35:00 2014 +0200 @@ -0,0 +1,59 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, + * 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 "IServerCommand.h" +#include "../ServerContext.h" +#include "../OrthancInitialization.h" + +namespace Orthanc +{ + class StorePeerCommand : public IServerCommand + { + private: + ServerContext& context_; + OrthancPeerParameters peer_; + + public: + StorePeerCommand(ServerContext& context, + const OrthancPeerParameters& peer); + + bool Apply(ListOfStrings& outputs, + const ListOfStrings& inputs); + + bool SendOutputsToSink() const + { + return false; + } + }; +}
--- a/OrthancServer/Scheduler/StoreScuCommand.cpp Tue Jul 08 15:11:00 2014 +0200 +++ b/OrthancServer/Scheduler/StoreScuCommand.cpp Tue Jul 08 17:35:00 2014 +0200 @@ -46,7 +46,6 @@ bool StoreScuCommand::Apply(ListOfStrings& outputs, const ListOfStrings& inputs) { - ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), modality_); for (ListOfStrings::const_iterator
--- a/OrthancServer/ServerContext.cpp Tue Jul 08 15:11:00 2014 +0200 +++ b/OrthancServer/ServerContext.cpp Tue Jul 08 17:35:00 2014 +0200 @@ -46,6 +46,7 @@ #include "Scheduler/DeleteInstanceCommand.h" #include "Scheduler/StoreScuCommand.h" +#include "Scheduler/StorePeerCommand.h" @@ -120,6 +121,39 @@ } + static IServerCommand* ParseOperation(ServerContext& context, + const std::string& operation, + const Json::Value& parameters) + { + if (operation == "delete") + { + LOG(INFO) << "Lua script to delete instance " << parameters["instance"].asString(); + return new DeleteInstanceCommand(context); + } + + if (operation == "store-scu") + { + 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)); + } + + if (operation == "store-peer") + { + std::string peer = parameters["peer"].asString(); + LOG(INFO) << "Lua script to send instance " << parameters["instance"].asString() + << " to peer " << peer << " using HTTP"; + + OrthancPeerParameters parameters; + Configuration::GetOrthancPeer(parameters, peer); + return new StorePeerCommand(context, parameters); + } + + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + void ServerContext::ApplyOnStoredInstance(const std::string& instanceId, const Json::Value& simplifiedDicom, const Json::Value& metadata) @@ -128,34 +162,59 @@ if (locker.GetLua().IsExistingFunction(ON_STORED_INSTANCE)) { + locker.GetLua().Execute("_InitializeJob()"); + LuaFunctionCall call(locker.GetLua(), ON_STORED_INSTANCE); call.PushString(instanceId); call.PushJson(simplifiedDicom); call.PushJson(metadata); - - Json::Value result; - call.ExecuteToJson(result); + call.Execute(); - printf("TODO\n"); - std::cout << result; - } - -#if 0 - { - // Autorouting test - RemoteModalityParameters p = Configuration::GetModalityUsingSymbolicName("sample"); + Json::Value operations; + LuaFunctionCall call2(locker.GetLua(), "_AccessJob"); + call2.ExecuteToJson(operations); + + if (operations.type() != Json::arrayValue) + { + throw OrthancException(ErrorCode_InternalError); + } ServerJob job; - ServerCommandInstance& a = job.AddCommand(new StoreScuCommand(*this, p)); - a.AddInput(instanceId); + ServerCommandInstance* previousCommand = NULL; + + for (Json::Value::ArrayIndex i = 0; i < operations.size(); ++i) + { + if (operations[i].type() != Json::objectValue || + !operations[i].isMember("operation")) + { + throw OrthancException(ErrorCode_InternalError); + } + + const Json::Value& parameters = operations[i]; + std::string operation = parameters["operation"].asString(); - /*ServerCommandInstance& b = job.AddCommand(new DeleteInstanceCommand(*this)); - a.ConnectNext(b);*/ + ServerCommandInstance& command = job.AddCommand(ParseOperation(*this, operation, operations[i])); + + if (parameters.isMember("instance") && + parameters["instance"].asString() != "") + { + command.AddInput(parameters["instance"].asString()); + } + else if (previousCommand != NULL) + { + previousCommand->ConnectNext(command); + } + else + { + throw OrthancException(ErrorCode_InternalError); + } - job.SetDescription("Autorouting test"); + previousCommand = &command; + } + + job.SetDescription(std::string("Lua script: ") + ON_STORED_INSTANCE); scheduler_.Submit(job); } -#endif } @@ -225,28 +284,21 @@ if (status == StoreStatus_Success || status == StoreStatus_AlreadyStored) { + Json::Value metadata = Json::objectValue; + for (std::map<MetadataType, std::string>::const_iterator + it = instanceMetadata.begin(); + it != instanceMetadata.end(); ++it) + { + metadata[EnumerationToString(it->first)] = it->second; + } + try { -#if 1 - Json::Value metadata = Json::objectValue; - for (std::map<MetadataType, std::string>::const_iterator - it = instanceMetadata.begin(); - it != instanceMetadata.end(); ++it) - { - metadata[EnumerationToString(it->first)] = it->second; - } -#else - Json::Value metadata; - index_.GetMetadata(metadata, resultPublicId); -#endif - - std::cout << metadata; - ApplyOnStoredInstance(resultPublicId, simplified, metadata); } - catch (OrthancException&) + catch (OrthancException& e) { - LOG(ERROR) << "Error when dealing with OnStoredInstance"; + LOG(ERROR) << "Lua error in OnStoredInstance: " << e.What(); } }
--- a/Resources/Samples/Lua/Autorouting.lua Tue Jul 08 15:11:00 2014 +0200 +++ b/Resources/Samples/Lua/Autorouting.lua Tue Jul 08 17:35:00 2014 +0200 @@ -1,9 +1,82 @@ -function OnStoredInstance(instance, tags, metadata) +function _InitializeJob() + _job = {} +end + +function _AccessJob() + return _job +end + +function SendToModality(instanceId, modality) + if instanceId == nil then + error('Cannot send an nonexistent instance') + end + + table.insert(_job, { + operation = 'store-scu', + instance = instanceId, + modality = modality + }) + return instanceId +end + +function SendToPeer(instanceId, peer) + if instanceId == nil then + error('Cannot send an nonexistent instance') + end + + table.insert(_job, { + operation = 'store-peer', + instance = instanceId, + peer = peer + }) + return instanceId +end + +function Delete(instanceId) + if instanceId == nil then + error('Cannot delete an nonexistent instance') + end + + table.insert(_job, { + operation = 'delete', + instance = instanceId + }) + return nil -- Forbid chaining +end + +function Modify(instanceId, replacements, removals, removePrivateTags) + if instanceId == nil then + error('Cannot modify an nonexistent instance') + end + + if instanceId == '' then + error('Cannot modify twice an instance'); + end + + table.insert(_job, { + operation = 'modify', + instance = instanceId, + replacements = replacements, + removals = removals, + removePrivateTags = removePrivateTags + }) + return '' -- Chain with another operation +end + + +function OnStoredInstance(instanceId, tags, metadata) --PrintRecursive(tags) - PrintRecursive(metadata) - print(metadata['RemoteAET']) - return { - { "store", instance, "pacs" }, - { "delete", instance } - } + --PrintRecursive(metadata) + --print(metadata['RemoteAET']) + + if true then + local patientName = string.lower(tags['PatientName']) + if string.find(patientName, 'david') ~= nil then + --Delete(SendToModality(instanceId, 'sample')) + --Delete(SendToPeer(instanceId, 'peer')) + Delete(SendToModality(Modify(instanceId, { PatientName = 'Hello^World' }), 'sample')) + else + Delete(instanceId) + end + end end