Mercurial > hg > orthanc
diff OrthancServer/ServerContext.cpp @ 1020:1fc112c4b832
integration lua-scripting->mainline
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 10 Jul 2014 11:38:46 +0200 |
parents | 160dfe770618 |
children | 921532f67770 |
line wrap: on
line diff
--- a/OrthancServer/ServerContext.cpp Thu Jul 10 11:26:05 2014 +0200 +++ b/OrthancServer/ServerContext.cpp Thu Jul 10 11:38:46 2014 +0200 @@ -43,9 +43,19 @@ #include <EmbeddedResources.h> #include <dcmtk/dcmdata/dcfilefo.h> + +#include "Scheduler/DeleteInstanceCommand.h" +#include "Scheduler/ModifyInstanceCommand.h" +#include "Scheduler/StoreScuCommand.h" +#include "Scheduler/StorePeerCommand.h" +#include "OrthancRestApi/OrthancRestApi.h" + + + #define ENABLE_DICOM_CACHE 1 static const char* RECEIVED_INSTANCE_FILTER = "ReceivedInstanceFilter"; +static const char* ON_STORED_INSTANCE = "OnStoredInstance"; static const size_t DICOM_CACHE_SIZE = 2; @@ -67,7 +77,8 @@ accessor_(storage_), compressionEnabled_(false), provider_(*this), - dicomCache_(provider_, DICOM_CACHE_SIZE) + dicomCache_(provider_, DICOM_CACHE_SIZE), + scheduler_(Configuration::GetGlobalIntegerParameter("LimitJobs", 10)) { scu_.SetLocalApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")); //scu_.SetMillisecondsBeforeClose(1); // The connection is always released @@ -90,76 +101,233 @@ storage_.Remove(fileUuid); } - StoreStatus ServerContext::Store(const char* dicomInstance, - size_t dicomSize, - const DicomMap& dicomSummary, - const Json::Value& dicomJson, - const std::string& remoteAet) + + bool ServerContext::ApplyReceivedInstanceFilter(const Json::Value& simplified, + const std::string& remoteAet) { - // Test if the instance must be filtered out - if (lua_.IsExistingFunction(RECEIVED_INSTANCE_FILTER)) + LuaContextLocker locker(*this); + + if (locker.GetLua().IsExistingFunction(RECEIVED_INSTANCE_FILTER)) { - Json::Value simplified; - SimplifyTags(simplified, dicomJson); - - LuaFunctionCall call(lua_, RECEIVED_INSTANCE_FILTER); - call.PushJSON(simplified); + LuaFunctionCall call(locker.GetLua(), RECEIVED_INSTANCE_FILTER); + call.PushJson(simplified); call.PushString(remoteAet); if (!call.ExecutePredicate()) { + return false; + } + } + + return true; + } + + + 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); + } + + if (operation == "modify") + { + LOG(INFO) << "Lua script to modify instance " << parameters["Instance"].asString(); + std::auto_ptr<ModifyInstanceCommand> command(new ModifyInstanceCommand(context)); + OrthancRestApi::ParseModifyRequest(command->GetModification(), parameters); + return command.release(); + } + + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + + void ServerContext::ApplyOnStoredInstance(const std::string& instanceId, + const Json::Value& simplifiedDicom, + const Json::Value& metadata) + { + LuaContextLocker locker(*this); + + 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); + call.Execute(); + + Json::Value operations; + LuaFunctionCall call2(locker.GetLua(), "_AccessJob"); + call2.ExecuteToJson(operations); + + if (operations.type() != Json::arrayValue) + { + throw OrthancException(ErrorCode_InternalError); + } + + ServerJob job; + 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& command = job.AddCommand(ParseOperation(*this, operation, operations[i])); + + if (!parameters.isMember("Instance")) + { + throw OrthancException(ErrorCode_InternalError); + } + + std::string instance = parameters["Instance"].asString(); + if (instance.empty()) + { + previousCommand->ConnectOutput(command); + } + else + { + command.AddInput(instance); + } + + previousCommand = &command; + } + + job.SetDescription(std::string("Lua script: ") + ON_STORED_INSTANCE); + scheduler_.Submit(job); + } + } + + + StoreStatus ServerContext::Store(std::string& resultPublicId, + DicomInstanceToStore& dicom) + { + try + { + DicomInstanceHasher hasher(dicom.GetSummary()); + resultPublicId = hasher.HashInstance(); + + Json::Value simplified; + SimplifyTags(simplified, dicom.GetJson()); + + // Test if the instance must be filtered out + if (!ApplyReceivedInstanceFilter(simplified, dicom.GetRemoteAet())) + { LOG(INFO) << "An incoming instance has been discarded by the filter"; return StoreStatus_FilteredOut; } - } + + if (compressionEnabled_) + { + accessor_.SetCompressionForNextOperations(CompressionType_Zlib); + } + else + { + accessor_.SetCompressionForNextOperations(CompressionType_None); + } + + FileInfo dicomInfo = accessor_.Write(dicom.GetBufferData(), dicom.GetBufferSize(), FileContentType_Dicom); + FileInfo jsonInfo = accessor_.Write(dicom.GetJson().toStyledString(), FileContentType_DicomAsJson); - if (compressionEnabled_) - { - accessor_.SetCompressionForNextOperations(CompressionType_Zlib); - } - else - { - accessor_.SetCompressionForNextOperations(CompressionType_None); - } + ServerIndex::Attachments attachments; + attachments.push_back(dicomInfo); + attachments.push_back(jsonInfo); + + std::map<MetadataType, std::string> instanceMetadata; + StoreStatus status = index_.Store(instanceMetadata, dicom.GetSummary(), attachments, + dicom.GetRemoteAet(), dicom.GetMetadata()); + + if (status != StoreStatus_Success) + { + storage_.Remove(dicomInfo.GetUuid()); + storage_.Remove(jsonInfo.GetUuid()); + } + + switch (status) + { + case StoreStatus_Success: + LOG(INFO) << "New instance stored"; + break; - FileInfo dicomInfo = accessor_.Write(dicomInstance, dicomSize, FileContentType_Dicom); - FileInfo jsonInfo = accessor_.Write(dicomJson.toStyledString(), FileContentType_DicomAsJson); + case StoreStatus_AlreadyStored: + LOG(INFO) << "Already stored"; + break; + + case StoreStatus_Failure: + LOG(ERROR) << "Store failure"; + break; + + default: + // This should never happen + break; + } - ServerIndex::Attachments attachments; - attachments.push_back(dicomInfo); - attachments.push_back(jsonInfo); + 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; + } - StoreStatus status = index_.Store(dicomSummary, attachments, remoteAet); + try + { + ApplyOnStoredInstance(resultPublicId, simplified, metadata); + } + catch (OrthancException& e) + { + LOG(ERROR) << "Error in OnStoredInstance callback (Lua): " << e.What(); + } + } - if (status != StoreStatus_Success) + return status; + } + catch (OrthancException& e) { - storage_.Remove(dicomInfo.GetUuid()); - storage_.Remove(jsonInfo.GetUuid()); - } - - switch (status) - { - case StoreStatus_Success: - LOG(INFO) << "New instance stored"; - break; + if (e.GetErrorCode() == ErrorCode_InexistentTag) + { + LogMissingRequiredTag(dicom.GetSummary()); + } - case StoreStatus_AlreadyStored: - LOG(INFO) << "Already stored"; - break; - - case StoreStatus_Failure: - LOG(ERROR) << "Store failure"; - break; - - default: - // This should never happen - break; + throw; } - - return status; } - + + void ServerContext::AnswerDicomFile(RestApiOutput& output, const std::string& instancePublicId, FileContentType content) @@ -249,87 +417,6 @@ } - static DcmFileFormat& GetDicom(ParsedDicomFile& file) - { - return *reinterpret_cast<DcmFileFormat*>(file.GetDcmtkObject()); - } - - - StoreStatus ServerContext::Store(std::string& resultPublicId, - ParsedDicomFile& dicomInstance, - const char* dicomBuffer, - size_t dicomSize) - { - DicomMap dicomSummary; - FromDcmtkBridge::Convert(dicomSummary, *GetDicom(dicomInstance).getDataset()); - - try - { - DicomInstanceHasher hasher(dicomSummary); - resultPublicId = hasher.HashInstance(); - - Json::Value dicomJson; - FromDcmtkBridge::ToJson(dicomJson, *GetDicom(dicomInstance).getDataset()); - - StoreStatus status = StoreStatus_Failure; - if (dicomSize > 0) - { - status = Store(dicomBuffer, dicomSize, dicomSummary, dicomJson, ""); - } - - return status; - } - catch (OrthancException& e) - { - if (e.GetErrorCode() == ErrorCode_InexistentTag) - { - LogMissingRequiredTag(dicomSummary); - } - - throw; - } - } - - - StoreStatus ServerContext::Store(std::string& resultPublicId, - ParsedDicomFile& dicomInstance) - { - std::string buffer; - if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer, GetDicom(dicomInstance).getDataset())) - { - throw OrthancException(ErrorCode_InternalError); - } - - if (buffer.size() == 0) - return Store(resultPublicId, dicomInstance, NULL, 0); - else - return Store(resultPublicId, dicomInstance, &buffer[0], buffer.size()); - } - - - StoreStatus ServerContext::Store(std::string& resultPublicId, - const char* dicomBuffer, - size_t dicomSize) - { - ParsedDicomFile dicom(dicomBuffer, dicomSize); - return Store(resultPublicId, dicom, dicomBuffer, dicomSize); - } - - - StoreStatus ServerContext::Store(std::string& resultPublicId, - const std::string& dicomContent) - { - if (dicomContent.size() == 0) - { - return Store(resultPublicId, NULL, 0); - } - else - { - return Store(resultPublicId, &dicomContent[0], dicomContent.size()); - } - } - - void ServerContext::SetStoreMD5ForAttachments(bool storeMD5) { LOG(INFO) << "Storing MD5 for attachments: " << (storeMD5 ? "yes" : "no");