# HG changeset patch # User Sebastien Jodogne # Date 1448024000 -3600 # Node ID 30e97a1f409347b701859a8363cb8bf52ad7431c # Parent 4f01c9d73f0245819c59cc8b449c84884a8d7aa2 callback for handling worklists with plugins diff -r 4f01c9d73f02 -r 30e97a1f4093 OrthancServer/DicomProtocol/DicomFindAnswers.h --- a/OrthancServer/DicomProtocol/DicomFindAnswers.h Fri Nov 20 12:57:14 2015 +0100 +++ b/OrthancServer/DicomProtocol/DicomFindAnswers.h Fri Nov 20 13:53:20 2015 +0100 @@ -42,10 +42,15 @@ class Answer; std::vector answers_; + bool complete_; Answer& GetAnswerInternal(size_t index) const; public: + DicomFindAnswers() : complete_(true) + { + } + ~DicomFindAnswers() { Clear(); @@ -77,5 +82,15 @@ void ToJson(Json::Value& target, size_t index, bool simplify) const; + + bool IsComplete() const + { + return complete_; + } + + void SetComplete(bool isComplete) + { + complete_ = isComplete; + } }; } diff -r 4f01c9d73f02 -r 30e97a1f4093 OrthancServer/DicomProtocol/IFindRequestHandler.h --- a/OrthancServer/DicomProtocol/IFindRequestHandler.h Fri Nov 20 12:57:14 2015 +0100 +++ b/OrthancServer/DicomProtocol/IFindRequestHandler.h Fri Nov 20 13:53:20 2015 +0100 @@ -43,13 +43,7 @@ { } - /** - * Can throw exceptions. Returns "false" iff too many results have - * to be returned. In such a case, a "Matching terminated due to - * Cancel request" DIMSE code would be returned. - * https://www.dabsoft.ch/dicom/4/V.4.1/ - **/ - virtual bool Handle(DicomFindAnswers& answers, + virtual void Handle(DicomFindAnswers& answers, const DicomMap& input, const std::string& remoteIp, const std::string& remoteAet, diff -r 4f01c9d73f02 -r 30e97a1f4093 OrthancServer/DicomProtocol/IWorklistRequestHandler.h --- a/OrthancServer/DicomProtocol/IWorklistRequestHandler.h Fri Nov 20 12:57:14 2015 +0100 +++ b/OrthancServer/DicomProtocol/IWorklistRequestHandler.h Fri Nov 20 13:53:20 2015 +0100 @@ -43,13 +43,7 @@ { } - /** - * Can throw exceptions. Returns "false" iff too many results have - * to be returned. In such a case, a "Matching terminated due to - * Cancel request" DIMSE code would be returned. - * https://www.dabsoft.ch/dicom/4/V.4.1/ - **/ - virtual bool Handle(DicomFindAnswers& answers, + virtual void Handle(DicomFindAnswers& answers, ParsedDicomFile& query, const std::string& remoteIp, const std::string& remoteAet, diff -r 4f01c9d73f02 -r 30e97a1f4093 OrthancServer/Internals/CommandDispatcher.cpp --- a/OrthancServer/Internals/CommandDispatcher.cpp Fri Nov 20 12:57:14 2015 +0100 +++ b/OrthancServer/Internals/CommandDispatcher.cpp Fri Nov 20 13:53:20 2015 +0100 @@ -803,7 +803,11 @@ { std::auto_ptr handler (server_.GetStoreRequestHandlerFactory().ConstructStoreRequestHandler()); - cond = Internals::storeScp(assoc_, &msg, presID, *handler, remoteIp_); + + if (handler.get() != NULL) + { + cond = Internals::storeScp(assoc_, &msg, presID, *handler, remoteIp_); + } } break; @@ -812,7 +816,11 @@ { std::auto_ptr handler (server_.GetMoveRequestHandlerFactory().ConstructMoveRequestHandler()); - cond = Internals::moveScp(assoc_, &msg, presID, *handler, remoteIp_, remoteAet_, calledAet_); + + if (handler.get() != NULL) + { + cond = Internals::moveScp(assoc_, &msg, presID, *handler, remoteIp_, remoteAet_, calledAet_); + } } break; diff -r 4f01c9d73f02 -r 30e97a1f4093 OrthancServer/Internals/FindScp.cpp --- a/OrthancServer/Internals/FindScp.cpp Fri Nov 20 12:57:14 2015 +0100 +++ b/OrthancServer/Internals/FindScp.cpp Fri Nov 20 13:53:20 2015 +0100 @@ -102,7 +102,6 @@ const std::string* remoteIp_; const std::string* remoteAet_; const std::string* calledAet_; - bool noCroppingOfResults_; }; @@ -135,9 +134,9 @@ if (data.worklistHandler_ != NULL) { ParsedDicomFile query(*requestIdentifiers); - data.noCroppingOfResults_ = data.worklistHandler_->Handle(data.answers_, query, - *data.remoteIp_, *data.remoteAet_, - *data.calledAet_); + data.worklistHandler_->Handle(data.answers_, query, + *data.remoteIp_, *data.remoteAet_, + *data.calledAet_); ok = true; } else @@ -151,9 +150,9 @@ { DicomMap input; FromDcmtkBridge::Convert(input, *requestIdentifiers); - data.noCroppingOfResults_ = data.findHandler_->Handle(data.answers_, input, - *data.remoteIp_, *data.remoteAet_, - *data.calledAet_); + data.findHandler_->Handle(data.answers_, input, + *data.remoteIp_, *data.remoteAet_, + *data.calledAet_); ok = true; } else @@ -191,7 +190,7 @@ response->DimseStatus = STATUS_Pending; *responseIdentifiers = data.answers_.ExtractDcmDataset(responseCount - 1); } - else if (data.noCroppingOfResults_) + else if (data.answers_.IsComplete()) { // Success: All the results have been sent response->DimseStatus = STATUS_Success; @@ -224,7 +223,6 @@ data.remoteIp_ = &remoteIp; data.remoteAet_ = &remoteAet; data.calledAet_ = &calledAet; - data.noCroppingOfResults_ = true; OFCondition cond = DIMSE_findProvider(assoc, presID, &msg->msg.CFindRQ, FindScpCallback, &data, diff -r 4f01c9d73f02 -r 30e97a1f4093 OrthancServer/OrthancFindRequestHandler.cpp --- a/OrthancServer/OrthancFindRequestHandler.cpp Fri Nov 20 12:57:14 2015 +0100 +++ b/OrthancServer/OrthancFindRequestHandler.cpp Fri Nov 20 13:53:20 2015 +0100 @@ -88,7 +88,7 @@ } - bool OrthancFindRequestHandler::Handle(DicomFindAnswers& answers, + void OrthancFindRequestHandler::Handle(DicomFindAnswers& answers, const DicomMap& input, const std::string& remoteIp, const std::string& remoteAet, @@ -195,7 +195,7 @@ context_.GetIndex().FindCandidates(resources, instances, finder); assert(resources.size() == instances.size()); - bool finished = true; + bool complete = true; for (size_t i = 0; i < instances.size(); i++) { @@ -207,7 +207,7 @@ if (maxResults != 0 && answers.GetSize() >= maxResults) { - finished = false; + complete = false; break; } else @@ -219,6 +219,6 @@ LOG(INFO) << "Number of matching resources: " << answers.GetSize(); - return finished; + answers.SetComplete(complete); } } diff -r 4f01c9d73f02 -r 30e97a1f4093 OrthancServer/OrthancFindRequestHandler.h --- a/OrthancServer/OrthancFindRequestHandler.h Fri Nov 20 12:57:14 2015 +0100 +++ b/OrthancServer/OrthancFindRequestHandler.h Fri Nov 20 13:53:20 2015 +0100 @@ -55,7 +55,7 @@ { } - virtual bool Handle(DicomFindAnswers& answers, + virtual void Handle(DicomFindAnswers& answers, const DicomMap& input, const std::string& remoteIp, const std::string& remoteAet, diff -r 4f01c9d73f02 -r 30e97a1f4093 OrthancServer/ParsedDicomFile.cpp --- a/OrthancServer/ParsedDicomFile.cpp Fri Nov 20 12:57:14 2015 +0100 +++ b/OrthancServer/ParsedDicomFile.cpp Fri Nov 20 13:53:20 2015 +0100 @@ -153,7 +153,8 @@ // This method can only be called from the constructors! - void ParsedDicomFile::Setup(const char* buffer, size_t size) + void ParsedDicomFile::Setup(const void* buffer, + size_t size) { DcmInputBufferStream is; if (size > 0) @@ -835,7 +836,8 @@ } - ParsedDicomFile::ParsedDicomFile(const char* content, size_t size) : pimpl_(new PImpl) + ParsedDicomFile::ParsedDicomFile(const void* content, + size_t size) : pimpl_(new PImpl) { Setup(content, size); } diff -r 4f01c9d73f02 -r 30e97a1f4093 OrthancServer/ParsedDicomFile.h --- a/OrthancServer/ParsedDicomFile.h Fri Nov 20 12:57:14 2015 +0100 +++ b/OrthancServer/ParsedDicomFile.h Fri Nov 20 13:53:20 2015 +0100 @@ -52,7 +52,7 @@ ParsedDicomFile(ParsedDicomFile& other); - void Setup(const char* content, + void Setup(const void* content, size_t size); void RemovePrivateTagsInternal(const std::set* toKeep); @@ -66,7 +66,7 @@ ParsedDicomFile(const DicomMap& map); - ParsedDicomFile(const char* content, + ParsedDicomFile(const void* content, size_t size); ParsedDicomFile(const std::string& content); diff -r 4f01c9d73f02 -r 30e97a1f4093 OrthancServer/main.cpp --- a/OrthancServer/main.cpp Fri Nov 20 12:57:14 2015 +0100 +++ b/OrthancServer/main.cpp Fri Nov 20 13:53:20 2015 +0100 @@ -107,7 +107,7 @@ { } - virtual bool Handle(DicomFindAnswers& answers, + virtual void Handle(DicomFindAnswers& answers, ParsedDicomFile& query, const std::string& remoteIp, const std::string& remoteAet, @@ -150,15 +150,14 @@ LOG(ERROR) << "Inexistent folder while scanning for worklists: " << source; } - return true; // All the worklists have been returned + answers.SetComplete(true); // All the worklists have been returned } }; class MyDicomServerFactory : public IStoreRequestHandlerFactory, - public IFindRequestHandlerFactory, - public IWorklistRequestHandlerFactory, + public IFindRequestHandlerFactory, public IMoveRequestHandlerFactory { private: @@ -209,11 +208,6 @@ return new OrthancMoveRequestHandler(context_); } - virtual IWorklistRequestHandler* ConstructWorklistRequestHandler() - { - return new OrthancWorklistRequestHandler(context_); - } - void Done() { } @@ -780,8 +774,12 @@ dicomServer.SetMoveRequestHandlerFactory(serverFactory); dicomServer.SetFindRequestHandlerFactory(serverFactory); - // TODO - Disable the following line if no worklist plugin is available - dicomServer.SetWorklistRequestHandlerFactory(serverFactory); +#if ORTHANC_PLUGINS_ENABLED == 1 + if (plugins) + { + dicomServer.SetWorklistRequestHandlerFactory(*plugins); + } +#endif dicomServer.SetPortNumber(Configuration::GetGlobalIntegerParameter("DicomPort", 4242)); dicomServer.SetApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")); diff -r 4f01c9d73f02 -r 30e97a1f4093 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Fri Nov 20 12:57:14 2015 +0100 +++ b/Plugins/Engine/OrthancPlugins.cpp Fri Nov 20 13:53:20 2015 +0100 @@ -237,6 +237,7 @@ typedef std::list RestCallbacks; typedef std::list OnStoredCallbacks; typedef std::list OnChangeCallbacks; + typedef std::list WorklistCallbacks; typedef std::map Properties; PluginsManager manager_; @@ -244,10 +245,12 @@ RestCallbacks restCallbacks_; OnStoredCallbacks onStoredCallbacks_; OnChangeCallbacks onChangeCallbacks_; + WorklistCallbacks worklistCallbacks_; std::auto_ptr storageArea_; boost::recursive_mutex restCallbackMutex_; boost::recursive_mutex storedCallbackMutex_; boost::recursive_mutex changeCallbackMutex_; + boost::recursive_mutex worklistCallbackMutex_; boost::recursive_mutex invokeServiceMutex_; Properties properties_; int argc_; @@ -622,6 +625,17 @@ } + void OrthancPlugins::RegisterWorklistCallback(const void* parameters) + { + const _OrthancPluginWorklistCallback& p = + *reinterpret_cast(parameters); + + LOG(INFO) << "Plugin has registered an modality worklist callback"; + pimpl_->worklistCallbacks_.push_back(p.callback); + } + + + void OrthancPlugins::AnswerBuffer(const void* parameters) { @@ -1401,6 +1415,10 @@ RegisterOnChangeCallback(parameters); return true; + case _OrthancPluginService_RegisterWorklistCallback: + RegisterWorklistCallback(parameters); + return true; + case _OrthancPluginService_AnswerBuffer: AnswerBuffer(parameters); return true; @@ -1831,6 +1849,16 @@ ApplyDicomToJson(service, parameters); return true; + case _OrthancPluginService_AddWorklistAnswer: + { + const _OrthancPluginAddWorklistAnswer& p = + *reinterpret_cast(parameters); + + ParsedDicomFile answer(p.answerDicom, p.answerSize); + reinterpret_cast(p.target)->Add(answer); + return true; + } + default: { // This service is unknown to the Orthanc plugin engine @@ -1948,4 +1976,72 @@ { return pimpl_->dictionary_; } + + + void OrthancPlugins::HandleWorklist(DicomFindAnswers& answers, + ParsedDicomFile& query, + const std::string& remoteIp, + const std::string& remoteAet, + const std::string& calledAet) + { + boost::recursive_mutex::scoped_lock lock(pimpl_->worklistCallbackMutex_); + + for (PImpl::WorklistCallbacks::const_iterator + callback = pimpl_->worklistCallbacks_.begin(); + callback != pimpl_->worklistCallbacks_.end(); ++callback) + { + OrthancPluginErrorCode error = (*callback) + (reinterpret_cast(&answers), + reinterpret_cast(&query), + remoteAet.c_str(), + calledAet.c_str()); + + if (error != OrthancPluginErrorCode_Success) + { + GetErrorDictionary().LogError(error, true); + throw OrthancException(static_cast(error)); + } + } + } + + + class OrthancPlugins::WorklistHandler : public IWorklistRequestHandler + { + private: + OrthancPlugins& plugins_; + + public: + WorklistHandler(OrthancPlugins& plugins) : plugins_(plugins) + { + } + + virtual void Handle(DicomFindAnswers& answers, + ParsedDicomFile& query, + const std::string& remoteIp, + const std::string& remoteAet, + const std::string& calledAet) + { + plugins_.HandleWorklist(answers, query, remoteIp, remoteAet, calledAet); + } + }; + + + IWorklistRequestHandler* OrthancPlugins::ConstructWorklistRequestHandler() + { + bool hasHandler; + + { + boost::recursive_mutex::scoped_lock lock(pimpl_->worklistCallbackMutex_); + hasHandler = !pimpl_->worklistCallbacks_.empty(); + } + + if (hasHandler) + { + return new WorklistHandler(*this); + } + else + { + return NULL; + } + } } diff -r 4f01c9d73f02 -r 30e97a1f4093 Plugins/Engine/OrthancPlugins.h --- a/Plugins/Engine/OrthancPlugins.h Fri Nov 20 12:57:14 2015 +0100 +++ b/Plugins/Engine/OrthancPlugins.h Fri Nov 20 13:53:20 2015 +0100 @@ -50,6 +50,7 @@ #include "../../Core/FileStorage/IStorageArea.h" #include "../../Core/HttpServer/IHttpHandler.h" #include "../../OrthancServer/IServerListener.h" +#include "../../OrthancServer/DicomProtocol/IWorklistRequestHandlerFactory.h" #include "OrthancPluginDatabase.h" #include "PluginsManager.h" @@ -63,12 +64,15 @@ class OrthancPlugins : public IHttpHandler, public IPluginServiceProvider, - public IServerListener + public IServerListener, + public IWorklistRequestHandlerFactory { private: struct PImpl; boost::shared_ptr pimpl_; + class WorklistHandler; + void CheckContextAvailable(); void RegisterRestCallback(const void* parameters, @@ -78,6 +82,8 @@ void RegisterOnChangeCallback(const void* parameters); + void RegisterWorklistCallback(const void* parameters); + void AnswerBuffer(const void* parameters); void Redirect(const void* parameters); @@ -138,6 +144,12 @@ OrthancPluginResourceType resourceType, const char* resource); + void HandleWorklist(DicomFindAnswers& answers, + ParsedDicomFile& query, + const std::string& remoteIp, + const std::string& remoteAet, + const std::string& calledAet); + public: OrthancPlugins(); @@ -204,6 +216,9 @@ { SignalChangeInternal(OrthancPluginChangeType_OrthancStopped, OrthancPluginResourceType_None, NULL); } + + + virtual IWorklistRequestHandler* ConstructWorklistRequestHandler(); }; } diff -r 4f01c9d73f02 -r 30e97a1f4093 Plugins/Include/orthanc/OrthancCPlugin.h --- a/Plugins/Include/orthanc/OrthancCPlugin.h Fri Nov 20 12:57:14 2015 +0100 +++ b/Plugins/Include/orthanc/OrthancCPlugin.h Fri Nov 20 13:53:20 2015 +0100 @@ -402,6 +402,7 @@ _OrthancPluginService_RegisterStorageArea = 1002, _OrthancPluginService_RegisterOnChangeCallback = 1003, _OrthancPluginService_RegisterRestCallbackNoLock = 1004, + _OrthancPluginService_RegisterWorklistCallback = 1005, /* Sending answers to REST calls */ _OrthancPluginService_AnswerBuffer = 2000, @@ -466,6 +467,9 @@ _OrthancPluginService_GetFontInfo = 6010, _OrthancPluginService_DrawText = 6011, + /* Primitives for handling worklists */ + _OrthancPluginService_AddWorklistAnswer = 7000, + _OrthancPluginService_INTERNAL = 0x7fffffff } _OrthancPluginService; @@ -873,14 +877,18 @@ * * Signature of a callback function that is triggered when Orthanc receives a C-Find SCP request. * + * @param answers The target structure where answers must be stored. + * @param query The worklist query. + * @param remoteAet The Application Entity Title (AET) of the modality from which the request originates. + * @param calledAet The Application Entity Title (AET) of the modality that is called by the request. * @return 0 if success, other value if error. * @ingroup Worklists **/ - typedef OrthancPluginErrorCode (*OrthancPluginFindWorklist) ( + typedef OrthancPluginErrorCode (*OrthancPluginWorklistCallback) ( OrthancPluginWorklistAnswers* answers, const OrthancPluginWorklistQuery* query, - const char* calledAet, - const char* remoteAet); + const char* remoteAet, + const char* calledAet); @@ -4061,6 +4069,58 @@ return context->InvokeService(context, _OrthancPluginService_RestApiGet2, ¶ms); } + + + typedef struct + { + OrthancPluginWorklistCallback callback; + } _OrthancPluginWorklistCallback; + + /** + * @brief Register a callback to handle modality worklists requests. + * + * This function registers a callback to handle C-Find SCP requests + * on modality worklists. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param callback The callback. + * @ingroup Worklists + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterWorklistCallback( + OrthancPluginContext* context, + OrthancPluginWorklistCallback callback) + { + _OrthancPluginWorklistCallback params; + params.callback = callback; + + context->InvokeService(context, _OrthancPluginService_RegisterWorklistCallback, ¶ms); + } + + + + typedef struct + { + OrthancPluginWorklistAnswers* target; + const void* answerDicom; + uint32_t answerSize; + } _OrthancPluginAddWorklistAnswer; + + + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginAddWorklistAnswer( + OrthancPluginContext* context, + OrthancPluginWorklistAnswers* target, + const void* answerDicom, + uint32_t answerSize) + { + _OrthancPluginAddWorklistAnswer params; + params.target = target; + params.answerDicom = answerDicom; + params.answerSize = answerSize; + + return context->InvokeService(context, _OrthancPluginService_AddWorklistAnswer, ¶ms); + } + + #ifdef __cplusplus } #endif