Mercurial > hg > orthanc
view OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h @ 5791:7fadeb395359
fixed incremented metrics that were not published + added orthanc_storage_cache_miss_count & orthanc_storage_cache_hit_count
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Wed, 18 Sep 2024 10:38:02 +0200 |
parents | f7adfb22e20e |
children |
line wrap: on
line source
/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, Belgium * Copyright (C) 2017-2023 Osimis S.A., Belgium * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, 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. * * 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 "OrthancPluginException.h" #include <orthanc/OrthancCPlugin.h> #include <boost/noncopyable.hpp> #include <boost/lexical_cast.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <json/value.h> #include <vector> #include <list> #include <set> #include <map> /** * The definition of ORTHANC_PLUGINS_VERSION_IS_ABOVE below is for * backward compatibility with Orthanc SDK <= 1.3.0. * * $ hg diff -r Orthanc-1.3.0:Orthanc-1.3.1 ../../../Plugins/Include/orthanc/OrthancCPlugin.h * **/ #if !defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) #define ORTHANC_PLUGINS_VERSION_IS_ABOVE(major, minor, revision) \ (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER > major || \ (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER == major && \ (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER > minor || \ (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER == minor && \ ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER >= revision)))) #endif #if !defined(ORTHANC_FRAMEWORK_VERSION_IS_ABOVE) #define ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(major, minor, revision) \ (ORTHANC_VERSION_MAJOR > major || \ (ORTHANC_VERSION_MAJOR == major && \ (ORTHANC_VERSION_MINOR > minor || \ (ORTHANC_VERSION_MINOR == minor && \ ORTHANC_VERSION_REVISION >= revision)))) #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 0) // The "OrthancPluginFindMatcher()" primitive was introduced in Orthanc 1.2.0 # define HAS_ORTHANC_PLUGIN_FIND_MATCHER 1 #else # define HAS_ORTHANC_PLUGIN_FIND_MATCHER 0 #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 4, 2) # define HAS_ORTHANC_PLUGIN_PEERS 1 # define HAS_ORTHANC_PLUGIN_JOB 1 #else # define HAS_ORTHANC_PLUGIN_PEERS 0 # define HAS_ORTHANC_PLUGIN_JOB 0 #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0) # define HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS 1 #else # define HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS 0 #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4) # define HAS_ORTHANC_PLUGIN_METRICS 1 #else # define HAS_ORTHANC_PLUGIN_METRICS 0 #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 1, 0) # define HAS_ORTHANC_PLUGIN_HTTP_CLIENT 1 #else # define HAS_ORTHANC_PLUGIN_HTTP_CLIENT 0 #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 7) # define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT 1 #else # define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT 0 #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 7) # define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER 1 #else # define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER 0 #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 0) # define HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP 1 #else # define HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP 0 #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 2) # define HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API 1 #else # define HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API 0 #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 10, 1) # define HAS_ORTHANC_PLUGIN_WEBDAV 1 #else # define HAS_ORTHANC_PLUGIN_WEBDAV 0 #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 4) # define HAS_ORTHANC_PLUGIN_LOG_MESSAGE 1 #else # define HAS_ORTHANC_PLUGIN_LOG_MESSAGE 0 #endif // Macro to tag a function as having been deprecated #if (__cplusplus >= 201402L) // C++14 # define ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED(f) [[deprecated]] f #elif defined(__GNUC__) || defined(__clang__) # define ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED(f) f __attribute__((deprecated)) #elif defined(_MSC_VER) # define ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED(f) __declspec(deprecated) f #else # define ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED #endif #if !defined(__ORTHANC_FILE__) # if defined(_MSC_VER) # pragma message("Warning: Macro __ORTHANC_FILE__ is not defined, this will leak the full path of the source files in the binaries") # else # warning Warning: Macro __ORTHANC_FILE__ is not defined, this will leak the full path of the source files in the binaries # endif # define __ORTHANC_FILE__ __FILE__ #endif #if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1 # define ORTHANC_PLUGINS_LOG_ERROR(msg) ::OrthancPlugins::LogMessage(OrthancPluginLogLevel_Error, __ORTHANC_FILE__, __LINE__, msg) # define ORTHANC_PLUGINS_LOG_WARNING(msg) ::OrthancPlugins::LogMessage(OrthancPluginLogLevel_Warning, __ORTHANC_FILE__, __LINE__, msg) # define ORTHANC_PLUGINS_LOG_INFO(msg) ::OrthancPlugins::LogMessage(OrthancPluginLogLevel_Info, __ORTHANC_FILE__, __LINE__, msg) #else # define ORTHANC_PLUGINS_LOG_ERROR(msg) ::OrthancPlugins::LogError(msg) # define ORTHANC_PLUGINS_LOG_WARNING(msg) ::OrthancPlugins::LogWarning(msg) # define ORTHANC_PLUGINS_LOG_INFO(msg) ::OrthancPlugins::LogInfo(msg) #endif namespace OrthancPlugins { typedef void (*RestCallback) (OrthancPluginRestOutput* output, const char* url, const OrthancPluginHttpRequest* request); void SetGlobalContext(OrthancPluginContext* context); void SetGlobalContext(OrthancPluginContext* context, const char* pluginName); void ResetGlobalContext(); bool HasGlobalContext(); OrthancPluginContext* GetGlobalContext(); class OrthancImage; class MemoryBuffer : public boost::noncopyable { private: OrthancPluginMemoryBuffer buffer_; void Check(OrthancPluginErrorCode code); bool CheckHttp(OrthancPluginErrorCode code); public: MemoryBuffer(); #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0) // This constructor makes a copy of the given buffer in the memory // handled by the Orthanc core MemoryBuffer(const void* buffer, size_t size); #endif ~MemoryBuffer() { Clear(); } OrthancPluginMemoryBuffer* operator*() { return &buffer_; } // This transfers ownership from "other" to "this" void Assign(OrthancPluginMemoryBuffer& other); void Swap(MemoryBuffer& other); OrthancPluginMemoryBuffer Release(); const char* GetData() const { if (buffer_.size > 0) { return reinterpret_cast<const char*>(buffer_.data); } else { return NULL; } } size_t GetSize() const { return buffer_.size; } bool IsEmpty() const { return GetSize() == 0 || GetData() == NULL; } void Clear(); void ToString(std::string& target) const; void ToJson(Json::Value& target) const; bool RestApiGet(const std::string& uri, bool applyPlugins); bool RestApiGet(const std::string& uri, const std::map<std::string, std::string>& httpHeaders, bool applyPlugins); bool RestApiPost(const std::string& uri, const void* body, size_t bodySize, bool applyPlugins); bool RestApiPut(const std::string& uri, const void* body, size_t bodySize, bool applyPlugins); bool RestApiPost(const std::string& uri, const Json::Value& body, bool applyPlugins); #if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1 bool RestApiPost(const std::string& uri, const Json::Value& body, const std::map<std::string, std::string>& httpHeaders, bool applyPlugins); bool RestApiPost(const std::string& uri, const void* body, size_t bodySize, const std::map<std::string, std::string>& httpHeaders, bool applyPlugins); #endif bool RestApiPut(const std::string& uri, const Json::Value& body, bool applyPlugins); bool RestApiPost(const std::string& uri, const std::string& body, bool applyPlugins) { return RestApiPost(uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins); } bool RestApiPut(const std::string& uri, const std::string& body, bool applyPlugins) { return RestApiPut(uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins); } void CreateDicom(const Json::Value& tags, OrthancPluginCreateDicomFlags flags); void CreateDicom(const Json::Value& tags, const OrthancImage& pixelData, OrthancPluginCreateDicomFlags flags); void ReadFile(const std::string& path); void GetDicomQuery(const OrthancPluginWorklistQuery* query); void DicomToJson(Json::Value& target, OrthancPluginDicomToJsonFormat format, OrthancPluginDicomToJsonFlags flags, uint32_t maxStringLength); bool HttpGet(const std::string& url, const std::string& username, const std::string& password); bool HttpPost(const std::string& url, const std::string& body, const std::string& username, const std::string& password); bool HttpPut(const std::string& url, const std::string& body, const std::string& username, const std::string& password); void GetDicomInstance(const std::string& instanceId); }; class OrthancString : public boost::noncopyable { private: char* str_; void Clear(); public: OrthancString() : str_(NULL) { } ~OrthancString() { Clear(); } // This transfers ownership, warning: The string must have been // allocated by the Orthanc core void Assign(char* str); const char* GetContent() const { return str_; } bool IsNullOrEmpty() const { return str_ == NULL || str_[0] == 0; } void ToString(std::string& target) const; void ToJson(Json::Value& target) const; void ToJsonWithoutComments(Json::Value& target) const; }; class OrthancConfiguration : public boost::noncopyable { private: Json::Value configuration_; // Necessarily a Json::objectValue std::string path_; std::string GetPath(const std::string& key) const; void LoadConfiguration(); public: OrthancConfiguration(); // loads the full Orthanc configuration explicit OrthancConfiguration(bool load); explicit OrthancConfiguration(const Json::Value& configuration, const std::string& path); // e.g. to load a section from a default json content const Json::Value& GetJson() const { return configuration_; } bool IsSection(const std::string& key) const; void GetSection(OrthancConfiguration& target, const std::string& key) const; bool LookupStringValue(std::string& target, const std::string& key) const; bool LookupIntegerValue(int& target, const std::string& key) const; bool LookupUnsignedIntegerValue(unsigned int& target, const std::string& key) const; bool LookupBooleanValue(bool& target, const std::string& key) const; bool LookupFloatValue(float& target, const std::string& key) const; bool LookupListOfStrings(std::list<std::string>& target, const std::string& key, bool allowSingleString) const; bool LookupSetOfStrings(std::set<std::string>& target, const std::string& key, bool allowSingleString) const; std::string GetStringValue(const std::string& key, const std::string& defaultValue) const; int GetIntegerValue(const std::string& key, int defaultValue) const; unsigned int GetUnsignedIntegerValue(const std::string& key, unsigned int defaultValue) const; bool GetBooleanValue(const std::string& key, bool defaultValue) const; float GetFloatValue(const std::string& key, float defaultValue) const; void GetDictionary(std::map<std::string, std::string>& target, const std::string& key) const; }; class OrthancImage : public boost::noncopyable { private: OrthancPluginImage* image_; void Clear(); void CheckImageAvailable() const; public: OrthancImage(); explicit OrthancImage(OrthancPluginImage* image); OrthancImage(OrthancPluginPixelFormat format, uint32_t width, uint32_t height); OrthancImage(OrthancPluginPixelFormat format, uint32_t width, uint32_t height, uint32_t pitch, void* buffer); ~OrthancImage() { Clear(); } void UncompressPngImage(const void* data, size_t size); void UncompressJpegImage(const void* data, size_t size); void DecodeDicomImage(const void* data, size_t size, unsigned int frame); OrthancPluginPixelFormat GetPixelFormat() const; unsigned int GetWidth() const; unsigned int GetHeight() const; unsigned int GetPitch() const; void* GetBuffer() const; const OrthancPluginImage* GetObject() const { return image_; } void CompressPngImage(MemoryBuffer& target) const; void CompressJpegImage(MemoryBuffer& target, uint8_t quality) const; void AnswerPngImage(OrthancPluginRestOutput* output) const; void AnswerJpegImage(OrthancPluginRestOutput* output, uint8_t quality) const; void* GetWriteableBuffer(); OrthancPluginImage* Release(); }; #if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1 class FindMatcher : public boost::noncopyable { private: OrthancPluginFindMatcher* matcher_; const OrthancPluginWorklistQuery* worklist_; void SetupDicom(const void* query, uint32_t size); public: explicit FindMatcher(const OrthancPluginWorklistQuery* worklist); FindMatcher(const void* query, uint32_t size) { SetupDicom(query, size); } explicit FindMatcher(const MemoryBuffer& dicom) { SetupDicom(dicom.GetData(), dicom.GetSize()); } ~FindMatcher(); bool IsMatch(const void* dicom, uint32_t size) const; bool IsMatch(const MemoryBuffer& dicom) const { return IsMatch(dicom.GetData(), dicom.GetSize()); } }; #endif bool ReadJson(Json::Value& target, const std::string& source); bool ReadJson(Json::Value& target, const void* buffer, size_t size); bool ReadJsonWithoutComments(Json::Value& target, const std::string& source); bool ReadJsonWithoutComments(Json::Value& target, const void* buffer, size_t size); void WriteFastJson(std::string& target, const Json::Value& source); void WriteStyledJson(std::string& target, const Json::Value& source); bool RestApiGet(Json::Value& result, const std::string& uri, bool applyPlugins); bool RestApiGet(Json::Value& result, const std::string& uri, const std::map<std::string, std::string>& httpHeaders, bool applyPlugins); bool RestApiGetString(std::string& result, const std::string& uri, bool applyPlugins); bool RestApiGetString(std::string& result, const std::string& uri, const std::map<std::string, std::string>& httpHeaders, bool applyPlugins); bool RestApiPost(std::string& result, const std::string& uri, const void* body, size_t bodySize, bool applyPlugins); bool RestApiPost(Json::Value& result, const std::string& uri, const void* body, size_t bodySize, bool applyPlugins); #if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1 bool RestApiPost(Json::Value& result, const std::string& uri, const Json::Value& body, const std::map<std::string, std::string>& httpHeaders, bool applyPlugins); #endif bool RestApiPost(Json::Value& result, const std::string& uri, const Json::Value& body, bool applyPlugins); inline bool RestApiPost(Json::Value& result, const std::string& uri, const std::string& body, bool applyPlugins) { return RestApiPost(result, uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins); } inline bool RestApiPost(Json::Value& result, const std::string& uri, const MemoryBuffer& body, bool applyPlugins) { return RestApiPost(result, uri, body.GetData(), body.GetSize(), applyPlugins); } bool RestApiPut(Json::Value& result, const std::string& uri, const void* body, size_t bodySize, bool applyPlugins); bool RestApiPut(Json::Value& result, const std::string& uri, const Json::Value& body, bool applyPlugins); inline bool RestApiPut(Json::Value& result, const std::string& uri, const std::string& body, bool applyPlugins) { return RestApiPut(result, uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins); } bool RestApiDelete(const std::string& uri, bool applyPlugins); bool HttpDelete(const std::string& url, const std::string& username, const std::string& password); void AnswerJson(const Json::Value& value, OrthancPluginRestOutput* output); void AnswerString(const std::string& answer, const char* mimeType, OrthancPluginRestOutput* output); void AnswerHttpError(uint16_t httpError, OrthancPluginRestOutput* output); void AnswerMethodNotAllowed(OrthancPluginRestOutput* output, const char* allowedMethods); #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0) const char* AutodetectMimeType(const std::string& path); #endif #if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1 void LogMessage(OrthancPluginLogLevel level, const char* file, uint32_t line, const std::string& message); #endif #if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1 // Use macro ORTHANC_PLUGINS_LOG_ERROR() instead ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED(void LogError(const std::string& message)); #else void LogError(const std::string& message); #endif #if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1 // Use macro ORTHANC_PLUGINS_LOG_WARNING() instead ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED(void LogWarning(const std::string& message)); #else void LogWarning(const std::string& message); #endif #if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1 // Use macro ORTHANC_PLUGINS_LOG_INFO() instead ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED(void LogInfo(const std::string& message)); #else void LogInfo(const std::string& message); #endif void ReportMinimalOrthancVersion(unsigned int major, unsigned int minor, unsigned int revision); bool CheckMinimalOrthancVersion(unsigned int major, unsigned int minor, unsigned int revision); bool CheckMinimalVersion(const char* version, unsigned int major, unsigned int minor, unsigned int revision); namespace Internals { template <RestCallback Callback> static OrthancPluginErrorCode Protect(OrthancPluginRestOutput* output, const char* url, const OrthancPluginHttpRequest* request) { try { Callback(output, url, request); return OrthancPluginErrorCode_Success; } catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) { #if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS == 1 if (HasGlobalContext() && e.HasDetails()) { // The "false" instructs Orthanc not to log the detailed // error message. This is to avoid duplicating the details, // because "OrthancException" already does it on construction. OrthancPluginSetHttpErrorDetails (GetGlobalContext(), output, e.GetDetails(), false); } #endif return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); } catch (boost::bad_lexical_cast&) { return OrthancPluginErrorCode_BadFileFormat; } catch (...) { return OrthancPluginErrorCode_Plugin; } } } template <RestCallback Callback> void RegisterRestCallback(const std::string& uri, bool isThreadSafe) { if (isThreadSafe) { OrthancPluginRegisterRestCallbackNoLock (GetGlobalContext(), uri.c_str(), Internals::Protect<Callback>); } else { OrthancPluginRegisterRestCallback (GetGlobalContext(), uri.c_str(), Internals::Protect<Callback>); } } #if HAS_ORTHANC_PLUGIN_PEERS == 1 class OrthancPeers : public boost::noncopyable { private: typedef std::map<std::string, uint32_t> Index; OrthancPluginPeers *peers_; Index index_; uint32_t timeout_; size_t GetPeerIndex(const std::string& name) const; public: OrthancPeers(); ~OrthancPeers(); uint32_t GetTimeout() const { return timeout_; } void SetTimeout(uint32_t timeout) { timeout_ = timeout; } bool LookupName(size_t& target, const std::string& name) const; std::string GetPeerName(size_t index) const; std::string GetPeerUrl(size_t index) const; std::string GetPeerUrl(const std::string& name) const; size_t GetPeersCount() const { return index_.size(); } bool LookupUserProperty(std::string& value, size_t index, const std::string& key) const; bool LookupUserProperty(std::string& value, const std::string& peer, const std::string& key) const; bool DoGet(MemoryBuffer& target, size_t index, const std::string& uri, const std::map<std::string, std::string>& headers) const; bool DoGet(MemoryBuffer& target, const std::string& name, const std::string& uri, const std::map<std::string, std::string>& headers) const; bool DoGet(Json::Value& target, size_t index, const std::string& uri, const std::map<std::string, std::string>& headers) const; bool DoGet(Json::Value& target, const std::string& name, const std::string& uri, const std::map<std::string, std::string>& headers) const; bool DoPost(MemoryBuffer& target, size_t index, const std::string& uri, const std::string& body, const std::map<std::string, std::string>& headers) const; bool DoPost(MemoryBuffer& target, const std::string& name, const std::string& uri, const std::string& body, const std::map<std::string, std::string>& headers) const; bool DoPost(Json::Value& target, size_t index, const std::string& uri, const std::string& body, const std::map<std::string, std::string>& headers) const; bool DoPost(Json::Value& target, const std::string& name, const std::string& uri, const std::string& body, const std::map<std::string, std::string>& headers) const; bool DoPut(size_t index, const std::string& uri, const std::string& body, const std::map<std::string, std::string>& headers) const; bool DoPut(const std::string& name, const std::string& uri, const std::string& body, const std::map<std::string, std::string>& headers) const; bool DoDelete(size_t index, const std::string& uri, const std::map<std::string, std::string>& headers) const; bool DoDelete(const std::string& name, const std::string& uri, const std::map<std::string, std::string>& headers) const; }; #endif #if HAS_ORTHANC_PLUGIN_JOB == 1 class OrthancJob : public boost::noncopyable { private: std::string jobType_; std::string content_; bool hasSerialized_; std::string serialized_; float progress_; static void CallbackFinalize(void* job); static float CallbackGetProgress(void* job); #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) static OrthancPluginErrorCode CallbackGetContent(OrthancPluginMemoryBuffer* target, void* job); #else static const char* CallbackGetContent(void* job); #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) static int32_t CallbackGetSerialized(OrthancPluginMemoryBuffer* target, void* job); #else static const char* CallbackGetSerialized(void* job); #endif static OrthancPluginJobStepStatus CallbackStep(void* job); static OrthancPluginErrorCode CallbackStop(void* job, OrthancPluginJobStopReason reason); static OrthancPluginErrorCode CallbackReset(void* job); protected: void ClearContent(); void UpdateContent(const Json::Value& content); void ClearSerialized(); void UpdateSerialized(const Json::Value& serialized); void UpdateProgress(float progress); public: explicit OrthancJob(const std::string& jobType); virtual ~OrthancJob() { } virtual OrthancPluginJobStepStatus Step() = 0; virtual void Stop(OrthancPluginJobStopReason reason) = 0; virtual void Reset() = 0; static OrthancPluginJob* Create(OrthancJob* job /* takes ownership */); static std::string Submit(OrthancJob* job /* takes ownership */, int priority); static void SubmitAndWait(Json::Value& result, OrthancJob* job /* takes ownership */, int priority); // Submit a job from a POST on the REST API with the same // conventions as in the Orthanc core (according to the // "Synchronous" and "Priority" options) static void SubmitFromRestApiPost(OrthancPluginRestOutput* output, const Json::Value& body, OrthancJob* job); }; #endif #if HAS_ORTHANC_PLUGIN_METRICS == 1 inline void SetMetricsValue(char* name, float value) { OrthancPluginSetMetricsValue(GetGlobalContext(), name, value, OrthancPluginMetricsType_Default); } class MetricsTimer : public boost::noncopyable { private: std::string name_; boost::posix_time::ptime start_; public: explicit MetricsTimer(const char* name); ~MetricsTimer(); }; #endif #if HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1 class HttpClient : public boost::noncopyable { public: typedef std::map<std::string, std::string> HttpHeaders; class IRequestBody : public boost::noncopyable { public: virtual ~IRequestBody() { } virtual bool ReadNextChunk(std::string& chunk) = 0; }; class IAnswer : public boost::noncopyable { public: virtual ~IAnswer() { } virtual void AddHeader(const std::string& key, const std::string& value) = 0; virtual void AddChunk(const void* data, size_t size) = 0; }; private: class RequestBodyWrapper; uint16_t httpStatus_; OrthancPluginHttpMethod method_; std::string url_; HttpHeaders headers_; std::string username_; std::string password_; uint32_t timeout_; std::string certificateFile_; std::string certificateKeyFile_; std::string certificateKeyPassword_; bool pkcs11_; std::string fullBody_; IRequestBody* chunkedBody_; bool allowChunkedTransfers_; #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1 void ExecuteWithStream(uint16_t& httpStatus, // out IAnswer& answer, // out IRequestBody& body) const; #endif void ExecuteWithoutStream(uint16_t& httpStatus, // out HttpHeaders& answerHeaders, // out std::string& answerBody, // out const std::string& body) const; public: HttpClient(); uint16_t GetHttpStatus() const { return httpStatus_; } void SetMethod(OrthancPluginHttpMethod method) { method_ = method; } const std::string& GetUrl() const { return url_; } void SetUrl(const std::string& url) { url_ = url; } void SetHeaders(const HttpHeaders& headers) { headers_ = headers; } void AddHeader(const std::string& key, const std::string& value) { headers_[key] = value; } void AddHeaders(const HttpHeaders& headers); void SetCredentials(const std::string& username, const std::string& password); void ClearCredentials(); void SetTimeout(unsigned int timeout) // 0 for default timeout { timeout_ = timeout; } void SetCertificate(const std::string& certificateFile, const std::string& keyFile, const std::string& keyPassword); void ClearCertificate(); void SetPkcs11(bool pkcs11) { pkcs11_ = pkcs11; } void ClearBody(); void SwapBody(std::string& body); void SetBody(const std::string& body); void SetBody(IRequestBody& body); // This function can be used to disable chunked transfers if the // remote server is Orthanc with a version <= 1.5.6. void SetChunkedTransfersAllowed(bool allow) { allowChunkedTransfers_ = allow; } bool IsChunkedTransfersAllowed() const { return allowChunkedTransfers_; } void Execute(IAnswer& answer); void Execute(HttpHeaders& answerHeaders /* out */, std::string& answerBody /* out */); void Execute(HttpHeaders& answerHeaders /* out */, Json::Value& answerBody /* out */); void Execute(); }; #endif class IChunkedRequestReader : public boost::noncopyable { public: virtual ~IChunkedRequestReader() { } virtual void AddChunk(const void* data, size_t size) = 0; virtual void Execute(OrthancPluginRestOutput* output) = 0; }; typedef IChunkedRequestReader* (*ChunkedRestCallback) (const char* url, const OrthancPluginHttpRequest* request); namespace Internals { void NullRestCallback(OrthancPluginRestOutput* output, const char* url, const OrthancPluginHttpRequest* request); IChunkedRequestReader *NullChunkedRestCallback(const char* url, const OrthancPluginHttpRequest* request); #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1 template <ChunkedRestCallback Callback> static OrthancPluginErrorCode ChunkedProtect(OrthancPluginServerChunkedRequestReader** reader, const char* url, const OrthancPluginHttpRequest* request) { try { if (reader == NULL) { return OrthancPluginErrorCode_InternalError; } else { *reader = reinterpret_cast<OrthancPluginServerChunkedRequestReader*>(Callback(url, request)); if (*reader == NULL) { return OrthancPluginErrorCode_Plugin; } else { return OrthancPluginErrorCode_Success; } } } catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) { return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); } catch (boost::bad_lexical_cast&) { return OrthancPluginErrorCode_BadFileFormat; } catch (...) { return OrthancPluginErrorCode_Plugin; } } OrthancPluginErrorCode ChunkedRequestReaderAddChunk( OrthancPluginServerChunkedRequestReader* reader, const void* data, uint32_t size); OrthancPluginErrorCode ChunkedRequestReaderExecute( OrthancPluginServerChunkedRequestReader* reader, OrthancPluginRestOutput* output); void ChunkedRequestReaderFinalize( OrthancPluginServerChunkedRequestReader* reader); #else OrthancPluginErrorCode ChunkedRestCompatibility(OrthancPluginRestOutput* output, const char* url, const OrthancPluginHttpRequest* request, RestCallback GetHandler, ChunkedRestCallback PostHandler, RestCallback DeleteHandler, ChunkedRestCallback PutHandler); template< RestCallback GetHandler, ChunkedRestCallback PostHandler, RestCallback DeleteHandler, ChunkedRestCallback PutHandler > inline OrthancPluginErrorCode ChunkedRestCompatibility(OrthancPluginRestOutput* output, const char* url, const OrthancPluginHttpRequest* request) { return ChunkedRestCompatibility(output, url, request, GetHandler, PostHandler, DeleteHandler, PutHandler); } #endif } // NB: We use a templated class instead of a templated function, because // default values are only available in functions since C++11 template< RestCallback GetHandler = Internals::NullRestCallback, ChunkedRestCallback PostHandler = Internals::NullChunkedRestCallback, RestCallback DeleteHandler = Internals::NullRestCallback, ChunkedRestCallback PutHandler = Internals::NullChunkedRestCallback > class ChunkedRestRegistration : public boost::noncopyable { public: static void Apply(const std::string& uri) { #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1 OrthancPluginRegisterChunkedRestCallback( GetGlobalContext(), uri.c_str(), GetHandler == Internals::NullRestCallback ? NULL : Internals::Protect<GetHandler>, PostHandler == Internals::NullChunkedRestCallback ? NULL : Internals::ChunkedProtect<PostHandler>, DeleteHandler == Internals::NullRestCallback ? NULL : Internals::Protect<DeleteHandler>, PutHandler == Internals::NullChunkedRestCallback ? NULL : Internals::ChunkedProtect<PutHandler>, Internals::ChunkedRequestReaderAddChunk, Internals::ChunkedRequestReaderExecute, Internals::ChunkedRequestReaderFinalize); #else OrthancPluginRegisterRestCallbackNoLock( GetGlobalContext(), uri.c_str(), Internals::ChunkedRestCompatibility<GetHandler, PostHandler, DeleteHandler, PutHandler>); #endif } }; #if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1 class IStorageCommitmentScpHandler : public boost::noncopyable { public: virtual ~IStorageCommitmentScpHandler() { } virtual OrthancPluginStorageCommitmentFailureReason Lookup(const std::string& sopClassUid, const std::string& sopInstanceUid) = 0; static OrthancPluginErrorCode Lookup(OrthancPluginStorageCommitmentFailureReason* target, void* rawHandler, const char* sopClassUid, const char* sopInstanceUid); static void Destructor(void* rawHandler); }; #endif class DicomInstance : public boost::noncopyable { private: bool toFree_; #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1) const OrthancPluginDicomInstance* instance_; #else OrthancPluginDicomInstance* instance_; #endif public: #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1) explicit DicomInstance(const OrthancPluginDicomInstance* instance); #else explicit DicomInstance(OrthancPluginDicomInstance* instance); #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0) DicomInstance(const void* buffer, size_t size); #endif ~DicomInstance(); const OrthancPluginDicomInstance* GetObject() const { return instance_; } std::string GetRemoteAet() const; const void* GetBuffer() const { return OrthancPluginGetInstanceData(GetGlobalContext(), instance_); } size_t GetSize() const { return static_cast<size_t>(OrthancPluginGetInstanceSize(GetGlobalContext(), instance_)); } void GetJson(Json::Value& target) const; void GetSimplifiedJson(Json::Value& target) const; OrthancPluginInstanceOrigin GetOrigin() const { return OrthancPluginGetInstanceOrigin(GetGlobalContext(), instance_); } #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1) std::string GetTransferSyntaxUid() const; #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1) bool HasPixelData() const; #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0) unsigned int GetFramesCount() const { return OrthancPluginGetInstanceFramesCount(GetGlobalContext(), instance_); } #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0) void GetRawFrame(std::string& target, unsigned int frameIndex) const; #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0) OrthancImage* GetDecodedFrame(unsigned int frameIndex) const; #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0) void Serialize(std::string& target) const; #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0) static DicomInstance* Transcode(const void* buffer, size_t size, const std::string& transferSyntax); #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 1) static DicomInstance* Load(const std::string& instanceId, OrthancPluginLoadDicomInstanceMode mode); #endif }; // helper method to convert Http headers from the plugin SDK to a std::map void GetHttpHeaders(std::map<std::string, std::string>& result, const OrthancPluginHttpRequest* request); #if HAS_ORTHANC_PLUGIN_WEBDAV == 1 class IWebDavCollection : public boost::noncopyable { public: class FileInfo { private: std::string name_; uint64_t contentSize_; std::string mime_; std::string dateTime_; public: FileInfo(const std::string& name, uint64_t contentSize, const std::string& dateTime) : name_(name), contentSize_(contentSize), dateTime_(dateTime) { } const std::string& GetName() const { return name_; } uint64_t GetContentSize() const { return contentSize_; } void SetMimeType(const std::string& mime) { mime_ = mime; } const std::string& GetMimeType() const { return mime_; } const std::string& GetDateTime() const { return dateTime_; } }; class FolderInfo { private: std::string name_; std::string dateTime_; public: FolderInfo(const std::string& name, const std::string& dateTime) : name_(name), dateTime_(dateTime) { } const std::string& GetName() const { return name_; } const std::string& GetDateTime() const { return dateTime_; } }; virtual ~IWebDavCollection() { } virtual bool IsExistingFolder(const std::vector<std::string>& path) = 0; virtual bool ListFolder(std::list<FileInfo>& files, std::list<FolderInfo>& subfolders, const std::vector<std::string>& path) = 0; virtual bool GetFile(std::string& content /* out */, std::string& mime /* out */, std::string& dateTime /* out */, const std::vector<std::string>& path) = 0; virtual bool StoreFile(const std::vector<std::string>& path, const void* data, size_t size) = 0; virtual bool CreateFolder(const std::vector<std::string>& path) = 0; virtual bool DeleteItem(const std::vector<std::string>& path) = 0; static void Register(const std::string& uri, IWebDavCollection& collection); }; #endif void SetRootUri(const std::string& pluginIdentifier, const std::string& uri); void SetDescription(const std::string& pluginIdentifier, const std::string& description); void ExtendOrthancExplorer(const std::string& pluginIdentifier, const std::string& javascript); }