Mercurial > hg > orthanc-stone
changeset 646:b4fe9642e83b
Merge from default
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Mon, 13 May 2019 15:22:08 +0200 |
parents | 1e9ed656318e (current diff) f0008c55e5f7 (diff) |
children | 6af3099ed8da f1bfe3d1759f |
files | Framework/Scene2D/Scene2D.cpp Samples/Sdl/Loader.cpp |
diffstat | 21 files changed, 897 insertions(+), 307 deletions(-) [+] |
line wrap: on
line diff
--- a/Framework/Layers/DicomSeriesVolumeSlicer.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Layers/DicomSeriesVolumeSlicer.h Mon May 13 15:22:08 2019 +0200 @@ -38,8 +38,10 @@ { public: // TODO: Add "frame" and "instanceId" - class FrameReadyMessage : public OriginMessage<MessageType_DicomSeriesVolumeSlicer_FrameReady, DicomSeriesVolumeSlicer> + class FrameReadyMessage : public OriginMessage<DicomSeriesVolumeSlicer> { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: const Orthanc::ImageAccessor& frame_; SliceImageQuality imageQuality_;
--- a/Framework/Layers/IVolumeSlicer.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Layers/IVolumeSlicer.h Mon May 13 15:22:08 2019 +0200 @@ -33,18 +33,20 @@ class IVolumeSlicer : public IObservable { public: - typedef OriginMessage<MessageType_VolumeSlicer_GeometryReady, IVolumeSlicer> GeometryReadyMessage; - typedef OriginMessage<MessageType_VolumeSlicer_GeometryError, IVolumeSlicer> GeometryErrorMessage; - typedef OriginMessage<MessageType_VolumeSlicer_ContentChanged, IVolumeSlicer> ContentChangedMessage; + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, IVolumeSlicer); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, IVolumeSlicer); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, IVolumeSlicer); - class SliceContentChangedMessage : public OriginMessage<MessageType_VolumeSlicer_SliceChanged, IVolumeSlicer> + class SliceContentChangedMessage : public OriginMessage<IVolumeSlicer> { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: const Slice& slice_; public: SliceContentChangedMessage(IVolumeSlicer& origin, - const Slice& slice) : + const Slice& slice) : OriginMessage(origin), slice_(slice) { @@ -57,8 +59,10 @@ }; - class LayerReadyMessage : public OriginMessage<MessageType_VolumeSlicer_LayerReady, IVolumeSlicer> + class LayerReadyMessage : public OriginMessage<IVolumeSlicer> { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + public: class IRendererFactory : public boost::noncopyable { @@ -96,8 +100,10 @@ }; - class LayerErrorMessage : public OriginMessage<MessageType_VolumeSlicer_LayerError, IVolumeSlicer> + class LayerErrorMessage : public OriginMessage<IVolumeSlicer> { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: const CoordinateSystem3D& slice_;
--- a/Framework/Messages/ICallable.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Messages/ICallable.h Mon May 13 15:22:08 2019 +0200 @@ -41,7 +41,8 @@ virtual void Apply(const IMessage& message) = 0; - virtual MessageType GetMessageType() const = 0; + virtual const MessageIdentifier& GetMessageIdentifier() = 0; + virtual IObserver* GetObserver() const = 0; }; @@ -58,8 +59,8 @@ private: typedef void (TObserver::* MemberFunction) (const TMessage&); - TObserver& observer_; - MemberFunction function_; + TObserver& observer_; + MemberFunction function_; public: Callable(TObserver& observer, @@ -79,9 +80,9 @@ ApplyInternal(dynamic_cast<const TMessage&>(message)); } - virtual MessageType GetMessageType() const + virtual const MessageIdentifier& GetMessageIdentifier() { - return static_cast<MessageType>(TMessage::Type); + return TMessage::GetStaticIdentifier(); } virtual IObserver* GetObserver() const @@ -115,11 +116,6 @@ lambda_(dynamic_cast<const TMessage&>(message)); } - virtual MessageType GetMessageType() const - { - return static_cast<MessageType>(TMessage::Type); - } - virtual IObserver* GetObserver() const { return &observer_;
--- a/Framework/Messages/IMessage.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Messages/IMessage.h Mon May 13 15:22:08 2019 +0200 @@ -21,79 +21,79 @@ #pragma once -#include "../StoneEnumerations.h" +#include <boost/noncopyable.hpp> -#include <boost/noncopyable.hpp> +#include <string.h> namespace OrthancStone { - // base message that are exchanged between IObservable and IObserver - class IMessage : public boost::noncopyable + class MessageIdentifier { private: - MessageType messageType_; - - protected: - IMessage(MessageType messageType) : - messageType_(messageType) + const char* file_; + int line_; + + public: + MessageIdentifier(const char* file, + int line) : + file_(file), + line_(line) + { + } + + MessageIdentifier() : + file_(NULL), + line_(0) { } + + bool operator< (const MessageIdentifier& other) const + { + if (file_ == NULL) + { + return false; + } + else if (line_ != other.line_) + { + return line_ < other.line_; + } + else + { + return strcmp(file_, other.file_) < 0; + } + } + }; + + /** + * Base messages that are exchanged between IObservable and + * IObserver. Messages are distinguished by the "__FILE__" and + * "__LINE__" macro, as in "Orthanc::SQLite::StatementId". + **/ + class IMessage : public boost::noncopyable + { public: virtual ~IMessage() { } - virtual MessageType GetType() const - { - return messageType_; - } + virtual const MessageIdentifier& GetIdentifier() const = 0; }; - // base class to derive from to implement your own messages - // it handles the message type for you - template <MessageType type> - class BaseMessage : public IMessage - { - public: - enum - { - Type = type - }; - - BaseMessage() : - IMessage(static_cast<MessageType>(Type)) - { - } - }; - - - // simple message implementation when no payload is needed - // sample usage: - // typedef NoPayloadMessage<MessageType_VolumeSlicer_GeometryReady> GeometryReadyMessage; - template <MessageType type> - class NoPayloadMessage : public BaseMessage<type> - { - public: - NoPayloadMessage() : - BaseMessage<type>() - { - } - }; - - // simple message implementation when no payload is needed but the origin is required - // sample usage: - // typedef OriginMessage<MessageType_SliceLoader_GeometryError, OrthancSlicesLoader> SliceGeometryErrorMessage; - template <MessageType type, typename TOrigin> - class OriginMessage : public BaseMessage<type> + /** + * Simple message implementation when no payload is needed but the + * origin is required. Sample usage: + * typedef OriginMessage<OrthancSlicesLoader> SliceGeometryErrorMessage; + **/ + template <typename TOrigin> + class OriginMessage : public IMessage { private: - const TOrigin& origin_; + const TOrigin& origin_; public: OriginMessage(const TOrigin& origin) : - BaseMessage<type>(), origin_(origin) { } @@ -104,3 +104,36 @@ } }; } + + +#define ORTHANC_STONE_MESSAGE(FILE, LINE) \ + public: \ + static const ::OrthancStone::MessageIdentifier& GetStaticIdentifier() \ + { \ + static const ::OrthancStone::MessageIdentifier id(FILE, LINE); \ + return id; \ + } \ + \ + virtual const ::OrthancStone::MessageIdentifier& GetIdentifier() const \ + { \ + return GetStaticIdentifier(); \ + } + + +#define ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(FILE, LINE, NAME, ORIGIN) \ + class NAME : public ::OrthancStone::OriginMessage<ORIGIN> \ + { \ + ORTHANC_STONE_MESSAGE(FILE, LINE); \ + \ + NAME(const ORIGIN& origin) : \ + OriginMessage(origin) \ + { \ + } \ + }; + + +#define ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(FILE, LINE, NAME) \ + class NAME : public ::OrthancStone::IMessage \ + { \ + ORTHANC_STONE_MESSAGE(FILE, LINE); \ + };
--- a/Framework/Messages/IObservable.cpp Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Messages/IObservable.cpp Mon May 13 15:22:08 2019 +0200 @@ -59,9 +59,8 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); } - MessageType messageType = callable->GetMessageType(); - - callables_[messageType].insert(callable); + const MessageIdentifier& id = callable->GetMessageIdentifier(); + callables_[id].insert(callable); } void IObservable::Unregister(IObserver *observer) @@ -87,7 +86,7 @@ void IObservable::EmitMessageInternal(const IObserver* receiver, const IMessage& message) { - Callables::const_iterator found = callables_.find(message.GetType()); + Callables::const_iterator found = callables_.find(message.GetIdentifier()); if (found != callables_.end()) {
--- a/Framework/Messages/IObservable.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Messages/IObservable.h Mon May 13 15:22:08 2019 +0200 @@ -35,8 +35,9 @@ class IObservable : public boost::noncopyable { private: - typedef std::map<int, std::set<ICallable*> > Callables; - typedef std::set<IMessageForwarder*> Forwarders; + typedef std::map<MessageIdentifier, std::set<ICallable*> > Callables; + + typedef std::set<IMessageForwarder*> Forwarders; MessageBroker& broker_; Callables callables_;
--- a/Framework/Radiography/RadiographyLayer.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Radiography/RadiographyLayer.h Mon May 13 15:22:08 2019 +0200 @@ -55,18 +55,7 @@ friend class RadiographyScene; public: - class LayerEditedMessage : - public OriginMessage<MessageType_RadiographyLayer_Edited, RadiographyLayer> - { - private: - - public: - LayerEditedMessage(const RadiographyLayer& origin) : - OriginMessage(origin) - { - } - }; - + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, LayerEditedMessage, RadiographyLayer); class Geometry {
--- a/Framework/Radiography/RadiographyScene.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Radiography/RadiographyScene.h Mon May 13 15:22:08 2019 +0200 @@ -37,9 +37,10 @@ public IObservable { public: - class GeometryChangedMessage : - public OriginMessage<MessageType_RadiographyScene_GeometryChanged, RadiographyScene> + class GeometryChangedMessage : public OriginMessage<RadiographyScene> { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: RadiographyLayer& layer_; @@ -57,9 +58,10 @@ } }; - class ContentChangedMessage : - public OriginMessage<MessageType_RadiographyScene_ContentChanged, RadiographyScene> + class ContentChangedMessage : public OriginMessage<RadiographyScene> { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: RadiographyLayer& layer_; @@ -77,9 +79,10 @@ } }; - class LayerEditedMessage : - public OriginMessage<MessageType_RadiographyScene_LayerEdited, RadiographyScene> + class LayerEditedMessage : public OriginMessage<RadiographyScene> { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: const RadiographyLayer& layer_; @@ -95,20 +98,12 @@ { return layer_; } - }; - class WindowingChangedMessage : - public OriginMessage<MessageType_RadiographyScene_WindowingChanged, RadiographyScene> - { - public: - WindowingChangedMessage(const RadiographyScene& origin) : - OriginMessage(origin) - { - } - }; + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, WindowingChangedMessage, RadiographyScene); + class LayerAccessor : public boost::noncopyable { private:
--- a/Framework/Scene2D/ColorSceneLayer.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Scene2D/ColorSceneLayer.h Mon May 13 15:22:08 2019 +0200 @@ -35,17 +35,20 @@ uint8_t green_; uint8_t blue_; uint64_t revision_; + protected: void BumpRevision() { - // this is *not* thread-safe!!! + // this is *not* thread-safe!!! => (SJO) no problem, Stone assumes mono-threading revision_++; } + public: ColorSceneLayer() : red_(255), green_(255), - blue_(255) + blue_(255), + revision_(0) { }
--- a/Framework/Scene2D/Scene2D.cpp Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Scene2D/Scene2D.cpp Mon May 13 15:22:08 2019 +0200 @@ -102,8 +102,8 @@ void Scene2D::SetLayer(int depth, ISceneLayer* layer) // Takes ownership { - //LOG(INFO) << "SetLayer(" << depth << ", " << - // reinterpret_cast<intptr_t>(layer) << ")"; + LOG(INFO) << "SetLayer(" << depth << ", " << + reinterpret_cast<intptr_t>(layer) << ")"; std::auto_ptr<Item> item(new Item(layer, layerCounter_++)); if (layer == NULL)
--- a/Framework/StoneEnumerations.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/StoneEnumerations.h Mon May 13 15:22:08 2019 +0200 @@ -115,74 +115,6 @@ BitmapAnchor_TopRight }; - enum MessageType - { - MessageType_Widget_GeometryChanged, - MessageType_Widget_ContentChanged, - - MessageType_VolumeSlicer_GeometryReady, // instance tags have been loaded - MessageType_VolumeSlicer_GeometryError, - MessageType_VolumeSlicer_ContentChanged, - MessageType_VolumeSlicer_SliceChanged, - MessageType_VolumeSlicer_LayerReady, // layer is ready to be rendered - MessageType_VolumeSlicer_LayerError, - - MessageType_DicomSeriesVolumeSlicer_FrameReady, // pixels data of the frame have been loaded - - MessageType_SliceViewerWidget_DisplayedSlice, // The displayed slice has changed - - MessageType_SliceLoader_GeometryReady, - MessageType_SliceLoader_GeometryError, - MessageType_SliceLoader_ImageReady, - MessageType_SliceLoader_ImageError, - - MessageType_VolumeLoader_GeometryReady, - MessageType_VolumeLoader_GeometryError, - MessageType_VolumeLoader_ContentChanged, // Content of several slices in the loader has changed - - MessageType_SlicedVolume_GeometryReady, - MessageType_SlicedVolume_GeometryError, - MessageType_SlicedVolume_VolumeReady, - MessageType_SlicedVolume_ContentChanged, - MessageType_SlicedVolume_SliceContentChanged, - - MessageType_HttpRequestSuccess, - MessageType_HttpRequestError, - - MessageType_OrthancApi_InternalGetJsonResponseReady, - MessageType_OrthancApi_InternalGetJsonResponseError, - - MessageType_OrthancApi_GenericGetJson_Ready, - MessageType_OrthancApi_GenericGetBinary_Ready, - MessageType_OrthancApi_GenericHttpError_Ready, - MessageType_OrthancApi_GenericEmptyResponse_Ready, - - MessageType_RadiographyScene_GeometryChanged, - MessageType_RadiographyScene_ContentChanged, - MessageType_RadiographyScene_LayerEdited, - MessageType_RadiographyScene_WindowingChanged, - - MessageType_RadiographyLayer_Edited, - - MessageType_ViewportChanged, - - MessageType_Timeout, - - // used in unit tests only - MessageType_Test1, - MessageType_Test2, - - - - MessageType_OrthancRestApiCommand, - MessageType_GetOrthancImageCommand, - MessageType_GetOrthancWebViewerJpegCommand, - MessageType_OracleCommandExceptionMessage, - - MessageType_CustomMessage // Custom messages ids ust be greater than this (this one must remain in last position) - }; - - enum ControlPointType { ControlPoint_TopLeftCorner = 0,
--- a/Framework/Toolbox/IDelayedCallExecutor.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Toolbox/IDelayedCallExecutor.h Mon May 13 15:22:08 2019 +0200 @@ -39,8 +39,7 @@ MessageBroker& broker_; public: - - typedef NoPayloadMessage<MessageType_Timeout> TimeoutMessage; + ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(__FILE__, __LINE__, TimeoutMessage); IDelayedCallExecutor(MessageBroker& broker) : broker_(broker)
--- a/Framework/Toolbox/IWebService.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Toolbox/IWebService.h Mon May 13 15:22:08 2019 +0200 @@ -45,8 +45,10 @@ public: typedef std::map<std::string, std::string> HttpHeaders; - class HttpRequestSuccessMessage : public BaseMessage<MessageType_HttpRequestSuccess> + class HttpRequestSuccessMessage : public IMessage { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: const std::string& uri_; const void* answer_; @@ -97,8 +99,10 @@ }; - class HttpRequestErrorMessage : public BaseMessage<MessageType_HttpRequestError> + class HttpRequestErrorMessage : public IMessage { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: const std::string& uri_; const Orthanc::IDynamicObject* payload_;
--- a/Framework/Toolbox/OrthancApiClient.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Toolbox/OrthancApiClient.h Mon May 13 15:22:08 2019 +0200 @@ -35,9 +35,10 @@ public IObserver { public: - class JsonResponseReadyMessage : - public BaseMessage<MessageType_OrthancApi_GenericGetJson_Ready> + class JsonResponseReadyMessage : public IMessage { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: const std::string& uri_; const Json::Value& json_; @@ -72,9 +73,10 @@ }; - class BinaryResponseReadyMessage : - public BaseMessage<MessageType_OrthancApi_GenericGetBinary_Ready> + class BinaryResponseReadyMessage : public IMessage { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: const std::string& uri_; const void* answer_; @@ -117,9 +119,10 @@ }; - class EmptyResponseReadyMessage : - public BaseMessage<MessageType_OrthancApi_GenericEmptyResponse_Ready> + class EmptyResponseReadyMessage : public IMessage { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: const std::string& uri_; const Orthanc::IDynamicObject* payload_;
--- a/Framework/Toolbox/OrthancSlicesLoader.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Toolbox/OrthancSlicesLoader.h Mon May 13 15:22:08 2019 +0200 @@ -35,13 +35,14 @@ class OrthancSlicesLoader : public IObservable, public IObserver { public: - - typedef OriginMessage<MessageType_SliceLoader_GeometryReady, OrthancSlicesLoader> SliceGeometryReadyMessage; - typedef OriginMessage<MessageType_SliceLoader_GeometryError, OrthancSlicesLoader> SliceGeometryErrorMessage; + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SliceGeometryReadyMessage, OrthancSlicesLoader); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SliceGeometryErrorMessage, OrthancSlicesLoader); - class SliceImageReadyMessage : - public OriginMessage<MessageType_SliceLoader_ImageReady, OrthancSlicesLoader> + + class SliceImageReadyMessage : public OriginMessage<OrthancSlicesLoader> { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: unsigned int sliceIndex_; const Slice& slice_; @@ -84,9 +85,10 @@ }; - class SliceImageErrorMessage : - public OriginMessage<MessageType_SliceLoader_ImageError, OrthancSlicesLoader> + class SliceImageErrorMessage : public OriginMessage<OrthancSlicesLoader> { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: const Slice& slice_; unsigned int sliceIndex_;
--- a/Framework/Viewport/IViewport.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Viewport/IViewport.h Mon May 13 15:22:08 2019 +0200 @@ -35,7 +35,7 @@ class IViewport : public IObservable { public: - typedef OriginMessage<MessageType_ViewportChanged, IViewport> ViewportChangedMessage; + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ViewportChangedMessage, IViewport); IViewport(MessageBroker& broker) : IObservable(broker)
--- a/Framework/Volumes/ISlicedVolume.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Volumes/ISlicedVolume.h Mon May 13 15:22:08 2019 +0200 @@ -29,14 +29,16 @@ class ISlicedVolume : public IObservable { public: - typedef OriginMessage<MessageType_SlicedVolume_ContentChanged, ISlicedVolume> ContentChangedMessage; - typedef OriginMessage<MessageType_SlicedVolume_GeometryError, ISlicedVolume> GeometryErrorMessage; - typedef OriginMessage<MessageType_SlicedVolume_GeometryReady, ISlicedVolume> GeometryReadyMessage; - typedef OriginMessage<MessageType_SlicedVolume_VolumeReady, ISlicedVolume> VolumeReadyMessage; + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, ISlicedVolume); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, ISlicedVolume); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, ISlicedVolume); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, VolumeReadyMessage, ISlicedVolume); - class SliceContentChangedMessage : - public OriginMessage<MessageType_SlicedVolume_SliceContentChanged, ISlicedVolume> + + class SliceContentChangedMessage : public OriginMessage<ISlicedVolume> { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: size_t sliceIndex_; const Slice& slice_;
--- a/Framework/Volumes/IVolumeLoader.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Volumes/IVolumeLoader.h Mon May 13 15:22:08 2019 +0200 @@ -28,9 +28,9 @@ class IVolumeLoader : public IObservable { public: - typedef OriginMessage<MessageType_VolumeLoader_GeometryReady, IVolumeLoader> GeometryReadyMessage; - typedef OriginMessage<MessageType_VolumeLoader_GeometryError, IVolumeLoader> GeometryErrorMessage; - typedef OriginMessage<MessageType_VolumeLoader_ContentChanged, IVolumeLoader> ContentChangedMessage; + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, IVolumeLoader); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, IVolumeLoader); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, IVolumeLoader); IVolumeLoader(MessageBroker& broker) : IObservable(broker)
--- a/Framework/Widgets/SliceViewerWidget.h Mon May 13 15:12:56 2019 +0200 +++ b/Framework/Widgets/SliceViewerWidget.h Mon May 13 15:22:08 2019 +0200 @@ -36,12 +36,15 @@ public IObservable { public: - typedef OriginMessage<MessageType_Widget_GeometryChanged, SliceViewerWidget> GeometryChangedMessage; - typedef OriginMessage<MessageType_Widget_ContentChanged, SliceViewerWidget> ContentChangedMessage; + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryChangedMessage, SliceViewerWidget); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, SliceViewerWidget); + // TODO - Use this message in ReferenceLineSource - class DisplayedSliceMessage : public OriginMessage<MessageType_SliceViewerWidget_DisplayedSlice, SliceViewerWidget> + class DisplayedSliceMessage : public OriginMessage<SliceViewerWidget> { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: const Slice& slice_;
--- a/Samples/Sdl/Loader.cpp Mon May 13 15:12:56 2019 +0200 +++ b/Samples/Sdl/Loader.cpp Mon May 13 15:22:08 2019 +0200 @@ -28,17 +28,23 @@ #include "../../Framework/Volumes/ImageBuffer3D.h" // From Orthanc framework +#include <Core/Compression/GzipCompressor.h> +#include <Core/Compression/ZlibCompressor.h> #include <Core/DicomFormat/DicomArray.h> #include <Core/DicomFormat/DicomImageInformation.h> #include <Core/HttpClient.h> #include <Core/IDynamicObject.h> #include <Core/Images/Image.h> #include <Core/Images/ImageProcessing.h> +#include <Core/Images/JpegReader.h> +#include <Core/Images/PamReader.h> +#include <Core/Images/PngReader.h> #include <Core/Images/PngWriter.h> #include <Core/Logging.h> #include <Core/MultiThreading/SharedMessageQueue.h> #include <Core/OrthancException.h> #include <Core/Toolbox.h> +#include <Core/SystemToolbox.h> #include <json/reader.h> #include <json/value.h> @@ -56,7 +62,9 @@ public: enum Type { - Type_OrthancApi + Type_OrthancRestApi, + Type_GetOrthancImage, + Type_GetOrthancWebViewerJpeg }; virtual ~IOracleCommand() @@ -131,20 +139,56 @@ + class OracleCommandExceptionMessage : public OrthancStone::IMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const IOracleCommand& command_; + Orthanc::OrthancException exception_; + + public: + OracleCommandExceptionMessage(const IOracleCommand& command, + const Orthanc::OrthancException& exception) : + command_(command), + exception_(exception) + { + } + + OracleCommandExceptionMessage(const IOracleCommand& command, + const Orthanc::ErrorCode& error) : + command_(command), + exception_(error) + { + } + + const IOracleCommand& GetCommand() const + { + return command_; + } + + const Orthanc::OrthancException& GetException() const + { + return exception_; + } + }; + + typedef std::map<std::string, std::string> HttpHeaders; - class OrthancApiOracleCommand : public OracleCommandWithPayload + class OrthancRestApiCommand : public OracleCommandWithPayload { public: - class SuccessMessage : public OrthancStone::OriginMessage<OrthancStone::MessageType_HttpRequestSuccess, // TODO - OrthancApiOracleCommand> + class SuccessMessage : public OrthancStone::OriginMessage<OrthancRestApiCommand> { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + private: HttpHeaders headers_; std::string answer_; public: - SuccessMessage(const OrthancApiOracleCommand& command, + SuccessMessage(const OrthancRestApiCommand& command, const HttpHeaders& answerHeaders, std::string& answer /* will be swapped to avoid a memcpy() */) : OriginMessage(command), @@ -174,27 +218,6 @@ }; - class FailureMessage : public OrthancStone::OriginMessage<OrthancStone::MessageType_HttpRequestError, // TODO - OrthancApiOracleCommand> - { - private: - Orthanc::HttpStatus status_; - - public: - FailureMessage(const OrthancApiOracleCommand& command, - Orthanc::HttpStatus status) : - OriginMessage(command), - status_(status) - { - } - - Orthanc::HttpStatus GetHttpStatus() const - { - return status_; - } - }; - - private: Orthanc::HttpMethod method_; std::string uri_; @@ -203,10 +226,10 @@ unsigned int timeout_; std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> > successCallback_; - std::auto_ptr< OrthancStone::MessageHandler<FailureMessage> > failureCallback_; + std::auto_ptr< OrthancStone::MessageHandler<OracleCommandExceptionMessage> > failureCallback_; public: - OrthancApiOracleCommand() : + OrthancRestApiCommand() : method_(Orthanc::HttpMethod_Get), uri_("/"), timeout_(10) @@ -215,7 +238,7 @@ virtual Type GetType() const { - return Type_OrthancApi; + return Type_OrthancRestApi; } void SetMethod(Orthanc::HttpMethod method) @@ -239,6 +262,11 @@ body_ = writer.write(json); } + void SetHttpHeaders(const HttpHeaders& headers) + { + headers_ = headers; + } + void SetHttpHeader(const std::string& key, const std::string& value) { @@ -286,6 +314,425 @@ + + class GetOrthancImageCommand : public OracleCommandWithPayload + { + public: + class SuccessMessage : public OrthancStone::OriginMessage<GetOrthancImageCommand> + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + std::auto_ptr<Orthanc::ImageAccessor> image_; + Orthanc::MimeType mime_; + + public: + SuccessMessage(const GetOrthancImageCommand& command, + Orthanc::ImageAccessor* image, // Takes ownership + Orthanc::MimeType mime) : + OriginMessage(command), + image_(image), + mime_(mime) + { + if (image == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + const Orthanc::ImageAccessor& GetImage() const + { + return *image_; + } + + Orthanc::MimeType GetMimeType() const + { + return mime_; + } + }; + + + private: + std::string uri_; + HttpHeaders headers_; + unsigned int timeout_; + + std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> > successCallback_; + std::auto_ptr< OrthancStone::MessageHandler<OracleCommandExceptionMessage> > failureCallback_; + + public: + GetOrthancImageCommand() : + uri_("/"), + timeout_(10) + { + } + + virtual Type GetType() const + { + return Type_GetOrthancImage; + } + + void SetUri(const std::string& uri) + { + uri_ = uri; + } + + void SetHttpHeader(const std::string& key, + const std::string& value) + { + headers_[key] = value; + } + + const std::string& GetUri() const + { + return uri_; + } + + const HttpHeaders& GetHttpHeaders() const + { + return headers_; + } + + void SetTimeout(unsigned int seconds) + { + timeout_ = seconds; + } + + unsigned int GetTimeout() const + { + return timeout_; + } + + void ProcessHttpAnswer(IMessageEmitter& emitter, + const OrthancStone::IObserver& receiver, + const std::string& answer, + const HttpHeaders& answerHeaders) const + { + Orthanc::MimeType contentType = Orthanc::MimeType_Binary; + + for (HttpHeaders::const_iterator it = answerHeaders.begin(); + it != answerHeaders.end(); ++it) + { + std::string s; + Orthanc::Toolbox::ToLowerCase(s, it->first); + + if (s == "content-type") + { + contentType = Orthanc::StringToMimeType(it->second); + break; + } + } + + std::auto_ptr<Orthanc::ImageAccessor> image; + + switch (contentType) + { + case Orthanc::MimeType_Png: + { + image.reset(new Orthanc::PngReader); + dynamic_cast<Orthanc::PngReader&>(*image).ReadFromMemory(answer); + break; + } + + case Orthanc::MimeType_Pam: + { + image.reset(new Orthanc::PamReader); + dynamic_cast<Orthanc::PamReader&>(*image).ReadFromMemory(answer); + break; + } + + case Orthanc::MimeType_Jpeg: + { + image.reset(new Orthanc::JpegReader); + dynamic_cast<Orthanc::JpegReader&>(*image).ReadFromMemory(answer); + break; + } + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, + "Unsupported HTTP Content-Type for an image: " + + std::string(Orthanc::EnumerationToString(contentType))); + } + + SuccessMessage message(*this, image.release(), contentType); + emitter.EmitMessage(receiver, message); + } + }; + + + + class GetOrthancWebViewerJpegCommand : public OracleCommandWithPayload + { + public: + class SuccessMessage : public OrthancStone::OriginMessage<GetOrthancWebViewerJpegCommand> + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + std::auto_ptr<Orthanc::ImageAccessor> image_; + + public: + SuccessMessage(const GetOrthancWebViewerJpegCommand& command, + Orthanc::ImageAccessor* image) : // Takes ownership + OriginMessage(command), + image_(image) + { + if (image == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + const Orthanc::ImageAccessor& GetImage() const + { + return *image_; + } + }; + + private: + std::string instanceId_; + unsigned int frame_; + unsigned int quality_; + HttpHeaders headers_; + unsigned int timeout_; + Orthanc::PixelFormat expectedFormat_; + + std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> > successCallback_; + std::auto_ptr< OrthancStone::MessageHandler<OracleCommandExceptionMessage> > failureCallback_; + + public: + GetOrthancWebViewerJpegCommand() : + frame_(0), + quality_(95), + timeout_(10), + expectedFormat_(Orthanc::PixelFormat_Grayscale8) + { + } + + virtual Type GetType() const + { + return Type_GetOrthancWebViewerJpeg; + } + + void SetExpectedFormat(Orthanc::PixelFormat format) + { + expectedFormat_ = format; + } + + void SetInstance(const std::string& instanceId) + { + instanceId_ = instanceId; + } + + void SetFrame(unsigned int frame) + { + frame_ = frame; + } + + void SetQuality(unsigned int quality) + { + if (quality <= 0 || + quality > 100) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + quality_ = quality; + } + } + + void SetHttpHeader(const std::string& key, + const std::string& value) + { + headers_[key] = value; + } + + Orthanc::PixelFormat GetExpectedFormat() const + { + return expectedFormat_; + } + + const std::string& GetInstanceId() const + { + return instanceId_; + } + + unsigned int GetFrame() const + { + return frame_; + } + + unsigned int GetQuality() const + { + return quality_; + } + + const HttpHeaders& GetHttpHeaders() const + { + return headers_; + } + + void SetTimeout(unsigned int seconds) + { + timeout_ = seconds; + } + + unsigned int GetTimeout() const + { + return timeout_; + } + + std::string GetUri() const + { + return ("/web-viewer/instances/jpeg" + boost::lexical_cast<std::string>(quality_) + + "-" + instanceId_ + "_" + boost::lexical_cast<std::string>(frame_)); + } + + void ProcessHttpAnswer(IMessageEmitter& emitter, + const OrthancStone::IObserver& receiver, + const std::string& answer) const + { + // This code comes from older "OrthancSlicesLoader::ParseSliceImageJpeg()" + + Json::Value encoded; + + { + Json::Reader reader; + if (!reader.parse(answer, encoded)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + if (encoded.type() != Json::objectValue || + !encoded.isMember("Orthanc") || + encoded["Orthanc"].type() != Json::objectValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + const Json::Value& info = encoded["Orthanc"]; + if (!info.isMember("PixelData") || + !info.isMember("Stretched") || + !info.isMember("Compression") || + info["Compression"].type() != Json::stringValue || + info["PixelData"].type() != Json::stringValue || + info["Stretched"].type() != Json::booleanValue || + info["Compression"].asString() != "Jpeg") + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + bool isSigned = false; + bool isStretched = info["Stretched"].asBool(); + + if (info.isMember("IsSigned")) + { + if (info["IsSigned"].type() != Json::booleanValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + isSigned = info["IsSigned"].asBool(); + } + } + + std::auto_ptr<Orthanc::ImageAccessor> reader; + + { + std::string jpeg; + Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString()); + + reader.reset(new Orthanc::JpegReader); + dynamic_cast<Orthanc::JpegReader&>(*reader).ReadFromMemory(jpeg); + } + + if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image + { + if (expectedFormat_ != Orthanc::PixelFormat_RGB24) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + if (isSigned || isStretched) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + SuccessMessage message(*this, reader.release()); + emitter.EmitMessage(receiver, message); + return; + } + } + + if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + if (!isStretched) + { + if (expectedFormat_ != reader->GetFormat()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + SuccessMessage message(*this, reader.release()); + emitter.EmitMessage(receiver, message); + return; + } + } + + int32_t stretchLow = 0; + int32_t stretchHigh = 0; + + if (!info.isMember("StretchLow") || + !info.isMember("StretchHigh") || + info["StretchLow"].type() != Json::intValue || + info["StretchHigh"].type() != Json::intValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + stretchLow = info["StretchLow"].asInt(); + stretchHigh = info["StretchHigh"].asInt(); + + if (stretchLow < -32768 || + stretchHigh > 65535 || + (stretchLow < 0 && stretchHigh > 32767)) + { + // This range cannot be represented with a uint16_t or an int16_t + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + // Decode a grayscale JPEG 8bpp image coming from the Web viewer + std::auto_ptr<Orthanc::ImageAccessor> image + (new Orthanc::Image(expectedFormat_, reader->GetWidth(), reader->GetHeight(), false)); + + Orthanc::ImageProcessing::Convert(*image, *reader); + reader.reset(); + + float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f; + + if (!OrthancStone::LinearAlgebra::IsCloseToZero(scaling)) + { + float offset = static_cast<float>(stretchLow) / scaling; + Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true); + } + + SuccessMessage message(*this, image.release()); + emitter.EmitMessage(receiver, message); + } + }; + + + + + class NativeOracle : public IOracle { private: @@ -336,53 +783,115 @@ std::vector<boost::thread*> workers_; + void CopyHttpHeaders(Orthanc::HttpClient& client, + const HttpHeaders& headers) + { + for (HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ ) + { + client.AddHeader(it->first, it->second); + } + } + + + void DecodeAnswer(std::string& answer, + const HttpHeaders& headers) + { + Orthanc::HttpCompression contentEncoding = Orthanc::HttpCompression_None; + + for (HttpHeaders::const_iterator it = headers.begin(); + it != headers.end(); ++it) + { + std::string s; + Orthanc::Toolbox::ToLowerCase(s, it->first); + + if (s == "content-encoding") + { + if (it->second == "gzip") + { + contentEncoding = Orthanc::HttpCompression_Gzip; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, + "Unsupported HTTP Content-Encoding: " + it->second); + } + + break; + } + } + + if (contentEncoding == Orthanc::HttpCompression_Gzip) + { + std::string compressed; + answer.swap(compressed); + + Orthanc::GzipCompressor compressor; + compressor.Uncompress(answer, compressed.c_str(), compressed.size()); + } + } + + void Execute(const OrthancStone::IObserver& receiver, - const OrthancApiOracleCommand& command) + const OrthancRestApiCommand& command) { - Orthanc::HttpClient client(orthanc_, command.GetUri()); + Orthanc::HttpClient client(orthanc_, command.GetUri()); client.SetMethod(command.GetMethod()); client.SetTimeout(command.GetTimeout()); + CopyHttpHeaders(client, command.GetHttpHeaders()); + if (command.GetMethod() == Orthanc::HttpMethod_Post || command.GetMethod() == Orthanc::HttpMethod_Put) { client.SetBody(command.GetBody()); } - - { - const HttpHeaders& headers = command.GetHttpHeaders(); - for (HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ ) - { - client.AddHeader(it->first, it->second); - } - } + + std::string answer; + HttpHeaders answerHeaders; + client.ApplyAndThrowException(answer, answerHeaders); + + DecodeAnswer(answer, answerHeaders); + + OrthancRestApiCommand::SuccessMessage message(command, answerHeaders, answer); + emitter_.EmitMessage(receiver, message); + } + + + void Execute(const OrthancStone::IObserver& receiver, + const GetOrthancImageCommand& command) + { + Orthanc::HttpClient client(orthanc_, command.GetUri()); + client.SetTimeout(command.GetTimeout()); + + CopyHttpHeaders(client, command.GetHttpHeaders()); std::string answer; HttpHeaders answerHeaders; - - bool success; - try - { - success = client.Apply(answer, answerHeaders); - } - catch (Orthanc::OrthancException& e) - { - success = false; - } + client.ApplyAndThrowException(answer, answerHeaders); - if (success) - { - OrthancApiOracleCommand::SuccessMessage message(command, answerHeaders, answer); - emitter_.EmitMessage(receiver, message); - } - else - { - OrthancApiOracleCommand::FailureMessage message(command, client.GetLastStatus()); - emitter_.EmitMessage(receiver, message); - } + DecodeAnswer(answer, answerHeaders); + + command.ProcessHttpAnswer(emitter_, receiver, answer, answerHeaders); } + void Execute(const OrthancStone::IObserver& receiver, + const GetOrthancWebViewerJpegCommand& command) + { + Orthanc::HttpClient client(orthanc_, command.GetUri()); + client.SetTimeout(command.GetTimeout()); + + CopyHttpHeaders(client, command.GetHttpHeaders()); + + std::string answer; + HttpHeaders answerHeaders; + client.ApplyAndThrowException(answer, answerHeaders); + + DecodeAnswer(answer, answerHeaders); + + command.ProcessHttpAnswer(emitter_, receiver, answer); + } + void Step() { @@ -390,15 +899,27 @@ if (object.get() != NULL) { + printf("===========================> REQUEST\n"); + const Item& item = dynamic_cast<Item&>(*object); try { switch (item.GetCommand().GetType()) { - case IOracleCommand::Type_OrthancApi: + case IOracleCommand::Type_OrthancRestApi: + Execute(item.GetReceiver(), + dynamic_cast<const OrthancRestApiCommand&>(item.GetCommand())); + break; + + case IOracleCommand::Type_GetOrthancImage: Execute(item.GetReceiver(), - dynamic_cast<const OrthancApiOracleCommand&>(item.GetCommand())); + dynamic_cast<const GetOrthancImageCommand&>(item.GetCommand())); + break; + + case IOracleCommand::Type_GetOrthancWebViewerJpeg: + Execute(item.GetReceiver(), + dynamic_cast<const GetOrthancWebViewerJpegCommand&>(item.GetCommand())); break; default: @@ -408,10 +929,13 @@ catch (Orthanc::OrthancException& e) { LOG(ERROR) << "Exception within the oracle: " << e.What(); + emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage(item.GetCommand(), e)); } catch (...) { LOG(ERROR) << "Native exception within the oracle"; + emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage + (item.GetCommand(), Orthanc::ErrorCode_InternalError)); } } } @@ -562,8 +1086,15 @@ virtual void EmitMessage(const OrthancStone::IObserver& observer, const OrthancStone::IMessage& message) { - boost::unique_lock<boost::shared_mutex> lock(mutex_); - oracleObservable_.EmitMessage(observer, message); + try + { + boost::unique_lock<boost::shared_mutex> lock(mutex_); + oracleObservable_.EmitMessage(observer, message); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception while emitting a message: " << e.What(); + } } @@ -802,12 +1333,15 @@ OrthancStone::CoordinateSystem3D GetFrameGeometry(unsigned int frame) const { - if (frame >= imageInformation_.GetNumberOfFrames()) + if (frame == 0) + { + return geometry_; + } + else if (frame >= imageInformation_.GetNumberOfFrames()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - - if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) + else if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) { if (frame >= frameOffsets_.size()) { @@ -819,6 +1353,10 @@ geometry_.GetAxisX(), geometry_.GetAxisY()); } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } } bool FrameContainsPlane(unsigned int frame, @@ -921,10 +1459,10 @@ class MessageHandler : public Orthanc::IDynamicObject { public: - virtual void Handle(const OrthancApiOracleCommand::SuccessMessage& message) const = 0; + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const = 0; }; - void Handle(const OrthancApiOracleCommand::SuccessMessage& message) + void Handle(const OrthancRestApiCommand::SuccessMessage& message) { dynamic_cast<const MessageHandler&>(message.GetOrigin().GetPayload()).Handle(message); } @@ -941,7 +1479,7 @@ { } - virtual void Handle(const OrthancApiOracleCommand::SuccessMessage& message) const + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const { Json::Value value; message.ParseJsonBody(value); @@ -975,7 +1513,7 @@ { } - virtual void Handle(const OrthancApiOracleCommand::SuccessMessage& message) const + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const { Json::Value value; message.ParseJsonBody(value); @@ -1003,7 +1541,7 @@ active_(false) { oracle.RegisterObserverCallback( - new OrthancStone::Callable<AxialVolumeOrthancLoader, OrthancApiOracleCommand::SuccessMessage> + new OrthancStone::Callable<AxialVolumeOrthancLoader, OrthancRestApiCommand::SuccessMessage> (*this, &AxialVolumeOrthancLoader::Handle)); } @@ -1017,7 +1555,7 @@ active_ = true; - std::auto_ptr<Refactoring::OrthancApiOracleCommand> command(new Refactoring::OrthancApiOracleCommand); + std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand); command->SetUri("/series/" + seriesId + "/instances-tags"); command->SetPayload(new LoadSeriesGeometryHandler(*this)); @@ -1039,7 +1577,7 @@ // TODO => Should be part of a second call if needed - std::auto_ptr<Refactoring::OrthancApiOracleCommand> command(new Refactoring::OrthancApiOracleCommand); + std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand); command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3004-000c"); command->SetPayload(new LoadInstanceGeometryHandler(*this)); @@ -1054,7 +1592,7 @@ class Toto : public OrthancStone::IObserver { private: - void Handle(const Refactoring::OrthancApiOracleCommand::SuccessMessage& message) + void Handle(const Refactoring::OrthancRestApiCommand::SuccessMessage& message) { Json::Value v; message.ParseJsonBody(v); @@ -1062,9 +1600,30 @@ printf("ICI [%s]\n", v.toStyledString().c_str()); } - void Handle(const Refactoring::OrthancApiOracleCommand::FailureMessage& message) + void Handle(const Refactoring::GetOrthancImageCommand::SuccessMessage& message) + { + printf("IMAGE %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); + } + + void Handle(const Refactoring::GetOrthancWebViewerJpegCommand::SuccessMessage& message) + { + printf("WebViewer %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); + } + + void Handle(const Refactoring::OracleCommandExceptionMessage& message) { - printf("ERROR %d\n", message.GetHttpStatus()); + printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType()); + + switch (message.GetCommand().GetType()) + { + case Refactoring::IOracleCommand::Type_GetOrthancWebViewerJpeg: + printf("URI: [%s]\n", dynamic_cast<const Refactoring::GetOrthancWebViewerJpegCommand&> + (message.GetCommand()).GetUri().c_str()); + break; + + default: + break; + } } public: @@ -1073,7 +1632,19 @@ { oracle.RegisterObserverCallback (new OrthancStone::Callable - <Toto, Refactoring::OrthancApiOracleCommand::SuccessMessage>(*this, &Toto::Handle)); + <Toto, Refactoring::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle)); + + oracle.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, Refactoring::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle)); + + oracle.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, Refactoring::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle)); + + oracle.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, Refactoring::OracleCommandExceptionMessage>(*this, &Toto::Handle)); } }; @@ -1101,12 +1672,13 @@ oracle.Start(); + if (1) { Json::Value v = Json::objectValue; v["Level"] = "Series"; v["Query"] = Json::objectValue; - std::auto_ptr<Refactoring::OrthancApiOracleCommand> command(new Refactoring::OrthancApiOracleCommand); + std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand); command->SetMethod(Orthanc::HttpMethod_Post); command->SetUri("/tools/find"); command->SetBody(v); @@ -1114,11 +1686,64 @@ oracle.Schedule(*toto, command.release()); } + if (1) + { + std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); + oracle.Schedule(*toto, command.release()); + } + + if (1) + { + std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); + oracle.Schedule(*toto, command.release()); + } + + if (1) + { + std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); + oracle.Schedule(*toto, command.release()); + } + + if (1) + { + std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); + command->SetHttpHeader("Accept-Encoding", "gzip"); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); + oracle.Schedule(*toto, command.release()); + } + + if (1) + { + std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); + command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); + command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); + oracle.Schedule(*toto, command.release()); + } + + if (1) + { + std::auto_ptr<Refactoring::GetOrthancWebViewerJpegCommand> command(new Refactoring::GetOrthancWebViewerJpegCommand); + command->SetHttpHeader("Accept-Encoding", "gzip"); + command->SetInstance("e6c7c20b-c9f65d7e-0d76f2e2-830186f2-3e3c600e"); + command->SetQuality(90); + oracle.Schedule(*toto, command.release()); + } + + // 2017-11-17-Anonymized loader1->LoadSeries(oracle, "cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT loader2->LoadInstance(oracle, "41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE - boost::this_thread::sleep(boost::posix_time::seconds(1)); + LOG(WARNING) << "...Waiting for Ctrl-C..."; + Orthanc::SystemToolbox::ServerBarrier(); + //boost::this_thread::sleep(boost::posix_time::seconds(1)); oracle.Stop(); }
--- a/UnitTestsSources/TestMessageBroker.cpp Mon May 13 15:12:56 2019 +0200 +++ b/UnitTestsSources/TestMessageBroker.cpp Mon May 13 15:22:08 2019 +0200 @@ -34,32 +34,25 @@ using namespace OrthancStone; - enum CustomMessageType - { - CustomMessageType_First = MessageType_CustomMessage + 1, - - CustomMessageType_Completed, - CustomMessageType_Increment - }; - - class MyObservable : public IObservable { public: - struct MyCustomMessage: public BaseMessage<(MessageType) CustomMessageType_Completed> + struct MyCustomMessage : public IMessage { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + int payload_; - MyCustomMessage(int payload) - : BaseMessage(), - payload_(payload) - {} + MyCustomMessage(int payload) : + payload_(payload) + { + } }; - MyObservable(MessageBroker& broker) - : IObservable(broker) - {} - + MyObservable(MessageBroker& broker) : + IObservable(broker) + { + } }; class MyObserver : public IObserver @@ -94,15 +87,18 @@ class MyPromiseSource : public IObservable { Promise* currentPromise_; + public: - struct MyPromiseMessage: public BaseMessage<MessageType_Test1> + struct MyPromiseMessage: public IMessage { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + int increment; - MyPromiseMessage(int increment) - : BaseMessage(), - increment(increment) - {} + MyPromiseMessage(int increment) : + increment(increment) + { + } }; MyPromiseSource(MessageBroker& broker)