Mercurial > hg > orthanc
changeset 1066:bb82e5e818e9
OnStoredInstance callback in plugins
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 25 Jul 2014 18:39:02 +0200 |
parents | 921532f67770 |
children | ace99e272203 |
files | OrthancServer/ServerContext.cpp OrthancServer/ServerContext.h OrthancServer/main.cpp Plugins/Engine/PluginsHttpHandler.cpp Plugins/Engine/PluginsHttpHandler.h Plugins/OrthancCPlugin/OrthancCPlugin.h Plugins/Samples/Basic/Plugin.c |
diffstat | 7 files changed, 360 insertions(+), 35 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancServer/ServerContext.cpp Fri Jul 25 16:59:33 2014 +0200 +++ b/OrthancServer/ServerContext.cpp Fri Jul 25 18:39:02 2014 +0200 @@ -50,7 +50,7 @@ #include "Scheduler/StoreScuCommand.h" #include "Scheduler/StorePeerCommand.h" #include "OrthancRestApi/OrthancRestApi.h" - +#include "../Plugins/Engine/PluginsHttpHandler.h" #define ENABLE_DICOM_CACHE 1 @@ -79,7 +79,8 @@ compressionEnabled_(false), provider_(*this), dicomCache_(provider_, DICOM_CACHE_SIZE), - scheduler_(Configuration::GetGlobalIntegerParameter("LimitJobs", 10)) + scheduler_(Configuration::GetGlobalIntegerParameter("LimitJobs", 10)), + plugins_(NULL) { scu_.SetLocalApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")); //scu_.SetMillisecondsBeforeClose(1); // The connection is always released @@ -206,9 +207,9 @@ } - void ServerContext::ApplyOnStoredInstance(const std::string& instanceId, - const Json::Value& simplifiedDicom, - const Json::Value& metadata) + void ServerContext::ApplyLuaOnStoredInstance(const std::string& instanceId, + const Json::Value& simplifiedDicom, + const Json::Value& metadata) { LuaContextLocker locker(*this); @@ -347,12 +348,24 @@ try { - ApplyOnStoredInstance(resultPublicId, simplified, metadata); + ApplyLuaOnStoredInstance(resultPublicId, simplified, metadata); } catch (OrthancException& e) { LOG(ERROR) << "Error in OnStoredInstance callback (Lua): " << e.What(); } + + if (plugins_ != NULL) + { + try + { + plugins_->SignalStoredInstance(dicom, resultPublicId); + } + catch (OrthancException& e) + { + LOG(ERROR) << "Error in OnStoredInstance callback (Lua): " << e.What(); + } + } } return status;
--- a/OrthancServer/ServerContext.h Fri Jul 25 16:59:33 2014 +0200 +++ b/OrthancServer/ServerContext.h Fri Jul 25 18:39:02 2014 +0200 @@ -45,6 +45,8 @@ namespace Orthanc { + class PluginsHttpHandler; + /** * This class is responsible for maintaining the storage area on the * filesystem (including compression), as well as the index of the @@ -69,9 +71,9 @@ bool ApplyReceivedInstanceFilter(const Json::Value& simplified, const std::string& remoteAet); - void ApplyOnStoredInstance(const std::string& instanceId, - const Json::Value& simplifiedDicom, - const Json::Value& metadata); + void ApplyLuaOnStoredInstance(const std::string& instanceId, + const Json::Value& simplifiedDicom, + const Json::Value& metadata); FileStorage storage_; ServerIndex index_; @@ -86,6 +88,7 @@ boost::mutex luaMutex_; LuaContext lua_; + PluginsHttpHandler* plugins_; // TODO Turn it into a listener pattern (idem for Lua callbacks) public: class DicomCacheLocker : public boost::noncopyable @@ -183,5 +186,10 @@ { return scheduler_; } + + void SetPluginsHttpHandler(PluginsHttpHandler& plugin) + { + plugins_ = &plugin; + } }; }
--- a/OrthancServer/main.cpp Fri Jul 25 16:59:33 2014 +0200 +++ b/OrthancServer/main.cpp Fri Jul 25 18:39:02 2014 +0200 @@ -484,6 +484,7 @@ httpServer.RegisterHandler(staticResources); httpServer.RegisterHandler(restApi); httpPlugins.SetOrthancRestApi(restApi); + context.SetPluginsHttpHandler(httpPlugins); // GO !!! Start the requested servers if (Configuration::GetGlobalBoolParameter("HttpServerEnabled", true))
--- a/Plugins/Engine/PluginsHttpHandler.cpp Fri Jul 25 16:59:33 2014 +0200 +++ b/Plugins/Engine/PluginsHttpHandler.cpp Fri Jul 25 18:39:02 2014 +0200 @@ -37,6 +37,7 @@ #include "../../Core/Toolbox.h" #include "../../Core/HttpServer/HttpOutput.h" #include "../../Core/ImageFormats/PngWriter.h" +#include "../../OrthancServer/ServerToolbox.h" #include <boost/regex.hpp> #include <glog/logging.h> @@ -79,12 +80,14 @@ struct PluginsHttpHandler::PImpl { - typedef std::pair<boost::regex*, OrthancPluginRestCallback> Callback; - typedef std::list<Callback> Callbacks; + typedef std::pair<boost::regex*, OrthancPluginRestCallback> RestCallback; + typedef std::list<RestCallback> RestCallbacks; + typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks; ServerContext& context_; - Callbacks callbacks_; + RestCallbacks restCallbacks_; OrthancRestApi* restApi_; + OnStoredCallbacks onStoredCallbacks_; PImpl(ServerContext& context) : context_(context), restApi_(NULL) { @@ -92,6 +95,27 @@ }; + static char* CopyString(const std::string& str) + { + char *result = reinterpret_cast<char*>(malloc(str.size() + 1)); + if (result == NULL) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + if (str.size() == 0) + { + result[0] = '\0'; + } + else + { + memcpy(result, &str[0], str.size() + 1); + } + + return result; + } + + PluginsHttpHandler::PluginsHttpHandler(ServerContext& context) { pimpl_.reset(new PImpl(context)); @@ -100,8 +124,8 @@ PluginsHttpHandler::~PluginsHttpHandler() { - for (PImpl::Callbacks::iterator it = pimpl_->callbacks_.begin(); - it != pimpl_->callbacks_.end(); ++it) + for (PImpl::RestCallbacks::iterator it = pimpl_->restCallbacks_.begin(); + it != pimpl_->restCallbacks_.end(); ++it) { // Delete the regular expression associated with this callback delete it->first; @@ -142,8 +166,8 @@ // Loop over the callbacks registered by the plugins bool found = false; - for (PImpl::Callbacks::const_iterator it = pimpl_->callbacks_.begin(); - it != pimpl_->callbacks_.end() && !found; ++it) + for (PImpl::RestCallbacks::const_iterator it = pimpl_->restCallbacks_.begin(); + it != pimpl_->restCallbacks_.end() && !found; ++it) { // Check whether the regular expression associated to this // callback matches the URI @@ -247,6 +271,20 @@ } + void PluginsHttpHandler::SignalStoredInstance(DicomInstanceToStore& instance, + const std::string& instanceId) + { + for (PImpl::OnStoredCallbacks::const_iterator + callback = pimpl_->onStoredCallbacks_.begin(); + callback != pimpl_->onStoredCallbacks_.end(); ++callback) + { + (*callback) (reinterpret_cast<OrthancPluginDicomInstance*>(&instance), + instanceId.c_str()); + } + } + + + static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target, const void* data, size_t size) @@ -293,7 +331,18 @@ *reinterpret_cast<const _OrthancPluginRestCallback*>(parameters); LOG(INFO) << "Plugin has registered a REST callback on: " << p.pathRegularExpression; - pimpl_->callbacks_.push_back(std::make_pair(new boost::regex(p.pathRegularExpression), p.callback)); + pimpl_->restCallbacks_.push_back(std::make_pair(new boost::regex(p.pathRegularExpression), p.callback)); + } + + + + void PluginsHttpHandler::RegisterOnStoredInstanceCallback(const void* parameters) + { + const _OrthancPluginOnStoredInstanceCallback& p = + *reinterpret_cast<const _OrthancPluginOnStoredInstanceCallback*>(parameters); + + LOG(INFO) << "Plugin has registered an OnStoredInstance callback"; + pimpl_->onStoredCallbacks_.push_back(p.callback); } @@ -546,24 +595,53 @@ } - char* PluginsHttpHandler::CopyString(const std::string& str) const + static void AccessDicomInstance(_OrthancPluginService service, + const void* parameters) { - char *result = reinterpret_cast<char*>(malloc(str.size() + 1)); - if (result == NULL) + const _OrthancPluginAccessDicomInstance& p = + *reinterpret_cast<const _OrthancPluginAccessDicomInstance*>(parameters); + + DicomInstanceToStore& instance = + *reinterpret_cast<DicomInstanceToStore*>(p.instance); + + switch (service) { - throw OrthancException(ErrorCode_NotEnoughMemory); - } + case _OrthancPluginService_GetInstanceRemoteAet: + *p.resultString = instance.GetRemoteAet().c_str(); + return; + + case _OrthancPluginService_GetInstanceSize: + *p.resultInt64 = instance.GetBufferSize(); + return; + + case _OrthancPluginService_GetInstanceData: + *p.resultString = instance.GetBufferData(); + return; - if (str.size() == 0) - { - result[0] = '\0'; + case _OrthancPluginService_GetInstanceJson: + case _OrthancPluginService_GetInstanceSimplifiedJson: + { + Json::StyledWriter writer; + std::string s; + + if (service == _OrthancPluginService_GetInstanceJson) + { + s = writer.write(instance.GetJson()); + } + else + { + Json::Value simplified; + SimplifyTags(simplified, instance.GetJson()); + s = writer.write(simplified); + } + + *p.resultStringToFree = CopyString(s); + return; + } + + default: + throw OrthancException(ErrorCode_InternalError); } - else - { - memcpy(result, &str[0], str.size() + 1); - } - - return result; } @@ -576,6 +654,10 @@ RegisterRestCallback(parameters); return true; + case _OrthancPluginService_RegisterOnStoredInstanceCallback: + RegisterOnStoredInstanceCallback(parameters); + return true; + case _OrthancPluginService_AnswerBuffer: AnswerBuffer(parameters); return true; @@ -640,6 +722,14 @@ LookupResource(ResourceType_Instance, parameters); return true; + case _OrthancPluginService_GetInstanceRemoteAet: + case _OrthancPluginService_GetInstanceSize: + case _OrthancPluginService_GetInstanceData: + case _OrthancPluginService_GetInstanceJson: + case _OrthancPluginService_GetInstanceSimplifiedJson: + AccessDicomInstance(service, parameters); + return true; + default: return false; }
--- a/Plugins/Engine/PluginsHttpHandler.h Fri Jul 25 16:59:33 2014 +0200 +++ b/Plugins/Engine/PluginsHttpHandler.h Fri Jul 25 18:39:02 2014 +0200 @@ -50,9 +50,9 @@ boost::shared_ptr<PImpl> pimpl_; - char* CopyString(const std::string& str) const; + void RegisterRestCallback(const void* parameters); - void RegisterRestCallback(const void* parameters); + void RegisterOnStoredInstanceCallback(const void* parameters); void AnswerBuffer(const void* parameters); @@ -94,6 +94,9 @@ virtual bool InvokeService(_OrthancPluginService service, const void* parameters); + void SignalStoredInstance(DicomInstanceToStore& instance, + const std::string& instanceId); + void SetOrthancRestApi(OrthancRestApi& restApi); }; }
--- a/Plugins/OrthancCPlugin/OrthancCPlugin.h Fri Jul 25 16:59:33 2014 +0200 +++ b/Plugins/OrthancCPlugin/OrthancCPlugin.h Fri Jul 25 18:39:02 2014 +0200 @@ -238,6 +238,7 @@ /* Registration of callbacks */ _OrthancPluginService_RegisterRestCallback = 1000, + _OrthancPluginService_RegisterOnStoredInstanceCallback = 1001, /* Sending answers to REST calls */ _OrthancPluginService_AnswerBuffer = 2000, @@ -257,7 +258,16 @@ _OrthancPluginService_LookupPatient = 3005, _OrthancPluginService_LookupStudy = 3006, _OrthancPluginService_LookupSeries = 3007, - _OrthancPluginService_LookupInstance = 3008 + _OrthancPluginService_LookupInstance = 3008, + + /* Access to DICOM instances */ + _OrthancPluginService_GetInstanceRemoteAet = 4000, + _OrthancPluginService_GetInstanceSize = 4001, + _OrthancPluginService_GetInstanceData = 4002, + _OrthancPluginService_GetInstanceJson = 4003, + _OrthancPluginService_GetInstanceSimplifiedJson = 4004 + + /* + METADATA !!! */ } _OrthancPluginService; @@ -338,6 +348,14 @@ typedef struct _OrthancPluginRestOutput_t OrthancPluginRestOutput; + + /** + * @brief Opaque structure that represents a DICOM instance received by Orthanc. + **/ + typedef struct _OrthancPluginDicomInstance_t OrthancPluginDicomInstance; + + + /** * @brief Signature of a callback function that answers to a REST request. **/ @@ -347,6 +365,16 @@ const OrthancPluginHttpRequest* request); + + /** + * @brief Signature of a callback function that is triggered when Orthanc receives a DICOM instance. + **/ + typedef int32_t (*OrthancPluginOnStoredInstanceCallback) ( + OrthancPluginDicomInstance* instance, + const char* instanceId); + + + /** * @brief Opaque structure that contains information about the Orthanc core. **/ @@ -551,6 +579,32 @@ typedef struct { + OrthancPluginOnStoredInstanceCallback callback; + } _OrthancPluginOnStoredInstanceCallback; + + /** + * @brief Register a callback for received instances. + * + * This function registers a callback function that is called + * whenever a new DICOM instance is stored into the Orthanc core. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param callback The callback function. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterOnStoredInstanceCallback( + OrthancPluginContext* context, + OrthancPluginOnStoredInstanceCallback callback) + { + _OrthancPluginOnStoredInstanceCallback params; + params.callback = callback; + + context->InvokeService(context, _OrthancPluginService_RegisterOnStoredInstanceCallback, ¶ms); + } + + + + typedef struct + { OrthancPluginRestOutput* output; const char* answer; uint32_t answerSize; @@ -1045,6 +1099,134 @@ } + typedef struct + { + char** resultStringToFree; + const char** resultString; + int64_t* resultInt64; + const char* key; + OrthancPluginDicomInstance* instance; + } _OrthancPluginAccessDicomInstance; + + + ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceRemoteAet( + OrthancPluginContext* context, + OrthancPluginDicomInstance* instance) + { + const char* result; + + _OrthancPluginAccessDicomInstance params; + memset(¶ms, 0, sizeof(params)); + params.resultString = &result; + params.instance = instance; + + if (context->InvokeService(context, _OrthancPluginService_GetInstanceRemoteAet, ¶ms)) + { + return NULL; + } + else + { + return result; + } + } + + + ORTHANC_PLUGIN_INLINE int64_t OrthancPluginGetInstanceSize( + OrthancPluginContext* context, + OrthancPluginDicomInstance* instance) + { + int64_t size; + + _OrthancPluginAccessDicomInstance params; + memset(¶ms, 0, sizeof(params)); + params.resultInt64 = &size; + params.instance = instance; + + if (context->InvokeService(context, _OrthancPluginService_GetInstanceSize, ¶ms)) + { + return -1; + } + else + { + return size; + } + } + + + ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceData( + OrthancPluginContext* context, + OrthancPluginDicomInstance* instance) + { + const char* result; + + _OrthancPluginAccessDicomInstance params; + memset(¶ms, 0, sizeof(params)); + params.resultString = &result; + params.instance = instance; + + if (context->InvokeService(context, _OrthancPluginService_GetInstanceData, ¶ms)) + { + return NULL; + } + else + { + return result; + } + } + + + ORTHANC_PLUGIN_INLINE char* OrthancPluginGetInstanceJson( + OrthancPluginContext* context, + OrthancPluginDicomInstance* instance) + { + char* result; + + _OrthancPluginAccessDicomInstance params; + memset(¶ms, 0, sizeof(params)); + params.resultStringToFree = &result; + params.instance = instance; + + if (context->InvokeService(context, _OrthancPluginService_GetInstanceJson, ¶ms)) + { + return NULL; + } + else + { + return result; + } + } + + + ORTHANC_PLUGIN_INLINE char* OrthancPluginGetInstanceSimplifiedJson( + OrthancPluginContext* context, + OrthancPluginDicomInstance* instance) + { + char* result; + + _OrthancPluginAccessDicomInstance params; + memset(¶ms, 0, sizeof(params)); + params.resultStringToFree = &result; + params.instance = instance; + + if (context->InvokeService(context, _OrthancPluginService_GetInstanceSimplifiedJson, ¶ms)) + { + return NULL; + } + else + { + return result; + } + } + + + + + /* + TODO : METADATA !!! + TODO : DOCUMENTATION !!! + */ + + #ifdef __cplusplus } #endif
--- a/Plugins/Samples/Basic/Plugin.c Fri Jul 25 16:59:33 2014 +0200 +++ b/Plugins/Samples/Basic/Plugin.c Fri Jul 25 18:39:02 2014 +0200 @@ -165,6 +165,33 @@ } +ORTHANC_PLUGINS_API int32_t OnStoredCallback(OrthancPluginDicomInstance* instance, + const char* instanceId) +{ + char buffer[256]; + FILE* fp; + char* json; + + sprintf(buffer, "Just received a DICOM instance of size %d and ID %s from AET %s", + (int) OrthancPluginGetInstanceSize(context, instance), instanceId, + OrthancPluginGetInstanceRemoteAet(context, instance)); + + OrthancPluginLogWarning(context, buffer); + + fp = fopen("PluginReceivedInstance.dcm", "wb"); + fwrite(OrthancPluginGetInstanceData(context, instance), + OrthancPluginGetInstanceSize(context, instance), 1, fp); + fclose(fp); + + json = OrthancPluginGetInstanceSimplifiedJson(context, instance); + printf("[%s]\n", json); + OrthancPluginFreeString(context, json); + + return 0; +} + + + ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c) { const char* pathLocator = "\"Path\" : \""; @@ -193,8 +220,9 @@ OrthancPluginRegisterRestCallback(context, "/(plu.*)/hello", Callback1); OrthancPluginRegisterRestCallback(context, "/plu.*/image", Callback2); OrthancPluginRegisterRestCallback(context, "/plugin/instances/([^/]+)/info", Callback3); + OrthancPluginRegisterRestCallback(context, "/instances/([^/]+)/preview", Callback4); - OrthancPluginRegisterRestCallback(context, "/instances/([^/]+)/preview", Callback4); + OrthancPluginRegisterOnStoredInstanceCallback(context, OnStoredCallback); /* Make REST requests to the built-in Orthanc API */ OrthancPluginRestApiGet(context, &tmp, "/changes");