# HG changeset patch # User am@osimis.io # Date 1537277001 -7200 # Node ID b4abaeb783b1a33ea7d83e3311c43985e0b65c5d # Parent 3897f9f28cfa927eb8e6b35c235ec997fd6cf78e messaging refactoring almost complete: works fine in native diff -r 3897f9f28cfa -r b4abaeb783b1 Applications/Samples/SimpleViewerApplication.h --- a/Applications/Samples/SimpleViewerApplication.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Applications/Samples/SimpleViewerApplication.h Tue Sep 18 15:23:21 2018 +0200 @@ -268,7 +268,7 @@ if (parameters.count("studyId") < 1) { LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc"; - orthancApiClient_->GetJsonAsync("/studies", new Callable(*this, &SimpleViewerApplication::OnStudyListReceived)); + orthancApiClient_->GetJsonAsync("/studies", new Callable(*this, &SimpleViewerApplication::OnStudyListReceived)); } else { @@ -276,31 +276,31 @@ } } - void OnStudyListReceived(const OrthancApiClient::NewGetJsonResponseReadyMessage& message) + void OnStudyListReceived(const OrthancApiClient::JsonResponseReadyMessage& message) { - const Json::Value& response = message.response_; + const Json::Value& response = message.Response; if (response.isArray() && response.size() > 1) { SelectStudy(response[0].asString()); } } - void OnStudyReceived(const OrthancApiClient::NewGetJsonResponseReadyMessage& message) + void OnStudyReceived(const OrthancApiClient::JsonResponseReadyMessage& message) { - const Json::Value& response = message.response_; + const Json::Value& response = message.Response; if (response.isObject() && response["Series"].isArray()) { for (size_t i=0; i < response["Series"].size(); i++) { - orthancApiClient_->GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable(*this, &SimpleViewerApplication::OnSeriesReceived)); + orthancApiClient_->GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable(*this, &SimpleViewerApplication::OnSeriesReceived)); } } } - void OnSeriesReceived(const OrthancApiClient::NewGetJsonResponseReadyMessage& message) + void OnSeriesReceived(const OrthancApiClient::JsonResponseReadyMessage& message) { - const Json::Value& response = message.response_; + const Json::Value& response = message.Response; if (response.isObject() && response["Instances"].isArray() && response["Instances"].size() > 0) { @@ -338,7 +338,7 @@ void SelectStudy(const std::string& studyId) { - orthancApiClient_->GetJsonAsync("/studies/" + studyId, new Callable(*this, &SimpleViewerApplication::OnStudyReceived)); + orthancApiClient_->GetJsonAsync("/studies/" + studyId, new Callable(*this, &SimpleViewerApplication::OnStudyReceived)); } void OnWidgetGeometryChanged(const LayerWidget::GeometryChangedMessage& message) diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Layers/ILayerSource.h --- a/Framework/Layers/ILayerSource.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Layers/ILayerSource.h Tue Sep 18 15:23:21 2018 +0200 @@ -32,66 +32,38 @@ { public: - typedef NoPayloadMessage GeometryReadyMessage; - typedef NoPayloadMessage GeometryErrorMessage; - typedef NoPayloadMessage ContentChangedMessage; + typedef OriginMessage GeometryReadyMessage; + typedef OriginMessage GeometryErrorMessage; + typedef OriginMessage ContentChangedMessage; - struct SliceChangedMessage : public IMessage + struct SliceChangedMessage : public OriginMessage { const Slice& slice_; - SliceChangedMessage(const Slice& slice) - : IMessage(MessageType_LayerSource_SliceChanged), + SliceChangedMessage(ILayerSource& origin, const Slice& slice) + : OriginMessage(origin), slice_(slice) { } }; - struct LayerReadyMessage : public IMessage + struct LayerReadyMessage : public OriginMessage { - std::auto_ptr& layer_; + std::auto_ptr& renderer_; const CoordinateSystem3D& slice_; bool isError_; - LayerReadyMessage(std::auto_ptr& layer, + LayerReadyMessage(ILayerSource& origin, + std::auto_ptr& layer, const CoordinateSystem3D& slice, - bool isError) // TODO Shouldn't this be separate as NotifyLayerError? - : IMessage(MessageType_LayerSource_LayerReady), - layer_(layer), + bool isError // TODO Shouldn't this be separate as NotifyLayerError? + ) + : OriginMessage(origin), + renderer_(layer), slice_(slice), isError_(isError) { } }; - - // class IObserver : public boost::noncopyable - // { - // public: - // virtual ~IObserver() - // { - // } - - // // Triggered as soon as the source has enough information to - // // answer to "GetExtent()" - // virtual void NotifyGeometryReady(const ILayerSource& source) = 0; - - // virtual void NotifyGeometryError(const ILayerSource& source) = 0; - - // // Triggered if the content of several slices in the source - // // volume has changed - // virtual void NotifyContentChange(const ILayerSource& source) = 0; - - // // Triggered if the content of some individual slice in the - // // source volume has changed - // virtual void NotifySliceChange(const ILayerSource& source, - // const Slice& slice) = 0; - - // // The layer must be deleted by the observer that releases the - // // std::auto_ptr - // virtual void NotifyLayerReady(std::auto_ptr& layer, - // const ILayerSource& source, - // const CoordinateSystem3D& slice, - // bool isError) = 0; // TODO Shouldn't this be separate as NotifyLayerError? - // }; ILayerSource(MessageBroker& broker) : IObservable(broker) @@ -101,8 +73,6 @@ { } - // virtual void Register(IObserver& observer) = 0; - virtual bool GetExtent(std::vector& points, const CoordinateSystem3D& viewportSlice) = 0; diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Layers/LayerSourceBase.cpp --- a/Framework/Layers/LayerSourceBase.cpp Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Layers/LayerSourceBase.cpp Tue Sep 18 15:23:21 2018 +0200 @@ -27,22 +27,22 @@ { void LayerSourceBase::NotifyGeometryReady() { - EmitMessage(ILayerSource::GeometryReadyMessage()); + EmitMessage(ILayerSource::GeometryReadyMessage(*this)); } void LayerSourceBase::NotifyGeometryError() { - EmitMessage(ILayerSource::GeometryErrorMessage()); + EmitMessage(ILayerSource::GeometryErrorMessage(*this)); } void LayerSourceBase::NotifyContentChange() { - EmitMessage(ILayerSource::ContentChangedMessage()); + EmitMessage(ILayerSource::ContentChangedMessage(*this)); } void LayerSourceBase::NotifySliceChange(const Slice& slice) { - EmitMessage(ILayerSource::SliceChangedMessage(slice)); + EmitMessage(ILayerSource::SliceChangedMessage(*this, slice)); } void LayerSourceBase::NotifyLayerReady(ILayerRenderer* layer, @@ -50,7 +50,7 @@ bool isError) { std::auto_ptr renderer(layer); - EmitMessage(ILayerSource::LayerReadyMessage(renderer, slice, isError)); + EmitMessage(ILayerSource::LayerReadyMessage(*this, renderer, slice, isError)); } } diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Layers/OrthancFrameLayerSource.cpp --- a/Framework/Layers/OrthancFrameLayerSource.cpp Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Layers/OrthancFrameLayerSource.cpp Tue Sep 18 15:23:21 2018 +0200 @@ -61,17 +61,12 @@ LayerSourceBase::NotifyLayerReady(NULL, message.slice_.GetGeometry(), true); } - OrthancFrameLayerSource::OrthancFrameLayerSource(MessageBroker& broker, IWebService& orthanc) : + OrthancFrameLayerSource::OrthancFrameLayerSource(MessageBroker& broker, OrthancApiClient& orthanc) : LayerSourceBase(broker), IObserver(broker), - //OrthancSlicesLoader::ISliceLoaderObserver(broker), loader_(broker, orthanc), quality_(SliceImageQuality_FullPng) { -// DeclareHandledMessage(MessageType_SliceLoader_GeometryReady); -// DeclareHandledMessage(MessageType_SliceLoader_GeometryError); -// DeclareHandledMessage(MessageType_SliceLoader_ImageReady); -// DeclareHandledMessage(MessageType_SliceLoader_ImageError); loader_.RegisterObserverCallback(new Callable(*this, &OrthancFrameLayerSource::OnSliceGeometryReady)); loader_.RegisterObserverCallback(new Callable(*this, &OrthancFrameLayerSource::OnSliceGeometryError)); loader_.RegisterObserverCallback(new Callable(*this, &OrthancFrameLayerSource::OnSliceImageReady)); diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Layers/OrthancFrameLayerSource.h --- a/Framework/Layers/OrthancFrameLayerSource.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Layers/OrthancFrameLayerSource.h Tue Sep 18 15:23:21 2018 +0200 @@ -24,6 +24,7 @@ #include "LayerSourceBase.h" #include "../Toolbox/IWebService.h" #include "../Toolbox/OrthancSlicesLoader.h" +#include "../Toolbox/OrthancApiClient.h" namespace OrthancStone { @@ -40,7 +41,7 @@ SliceImageQuality quality_; public: - OrthancFrameLayerSource(MessageBroker& broker, IWebService& orthanc); + OrthancFrameLayerSource(MessageBroker& broker, OrthancApiClient& orthanc); void LoadSeries(const std::string& seriesId); diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Messages/IObservable.h --- a/Framework/Messages/IObservable.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Messages/IObservable.h Tue Sep 18 15:23:21 2018 +0200 @@ -34,17 +34,6 @@ namespace OrthancStone { - class MessageNotDeclaredException : public std::logic_error - { - MessageType messageType_; - public: - MessageNotDeclaredException(MessageType messageType) - : std::logic_error("Message not declared by observer."), - messageType_(messageType) - { - } - }; - class IObservable : public boost::noncopyable { protected: @@ -52,7 +41,6 @@ typedef std::map > Callables; Callables callables_; - std::set emittableMessages_; public: @@ -82,11 +70,6 @@ void EmitMessage(const IMessage& message) { - if (emittableMessages_.find(message.GetType()) == emittableMessages_.end()) - { - throw MessageNotDeclaredException(message.GetType()); - } - Callables::const_iterator found = callables_.find(message.GetType()); if (found != callables_.end()) @@ -101,16 +84,6 @@ } } } - const std::set& GetEmittableMessages() const - { - return emittableMessages_; - } - protected: - - void DeclareEmittableMessage(MessageType messageType) - { - emittableMessages_.insert(messageType); - } }; diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Messages/IObserver.h --- a/Framework/Messages/IObserver.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Messages/IObserver.h Tue Sep 18 15:23:21 2018 +0200 @@ -34,8 +34,6 @@ { protected: MessageBroker& broker_; - std::set handledMessages_; - std::set ignoredMessages_; public: IObserver(MessageBroker& broker) @@ -48,41 +46,6 @@ { broker_.Unregister(*this); } - -// void HandleMessage_(IObservable &from, const IMessage &message) -// { -// assert(handledMessages_.find(message.GetType()) != handledMessages_.end()); // please declare the messages that you're handling - -// HandleMessage(from, message); -// } - -// virtual void HandleMessage(IObservable& from, const IMessage& message) = 0; - - -// const std::set& GetHandledMessages() const -// { -// return handledMessages_; -// } - -// const std::set& GetIgnoredMessages() const -// { -// return ignoredMessages_; -// } - -// protected: - -// // when you connect an IObserver to an IObservable, the observer must handle all observable messages (this is checked during the registration) -// // so, all messages that may be emitted by the observable must be declared "handled" or "ignored" by the observer -// void DeclareHandledMessage(MessageType messageType) -// { -// handledMessages_.insert(messageType); -// } - -// void DeclareIgnoredMessage(MessageType messageType) -// { -// ignoredMessages_.insert(messageType); -// } - }; } diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Messages/MessageForwarder.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Messages/MessageForwarder.h Tue Sep 18 15:23:21 2018 +0200 @@ -0,0 +1,53 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ICallable.h" +#include "IObservable.h" +#include "IObserver.h" + +#include + +namespace OrthancStone { + + + template + class MessageForwarder : public IObserver, public Callable, TMessage> + { + IObservable& observable_; + public: + MessageForwarder(MessageBroker& broker, + IObservable& observable // the object that will emit the forwarded message + ) + : IObserver(broker), + Callable, TMessage>(*this, &MessageForwarder::ForwardMessage), + observable_(observable) + { + } + + protected: + void ForwardMessage(const TMessage& message) + { + observable_.EmitMessage(message); + } + }; +} diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Messages/MessageType.h --- a/Framework/Messages/MessageType.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Messages/MessageType.h Tue Sep 18 15:23:21 2018 +0200 @@ -45,6 +45,7 @@ MessageType_OrthancApi_InternalGetJsonResponseError, MessageType_OrthancApi_GenericGetJson_Ready, + MessageType_OrthancApi_GenericGetBinary_Ready, MessageType_OrthancApi_GenericHttpError_Ready, MessageType_OrthancApi_GetStudyIds_Ready, MessageType_OrthancApi_GetStudy_Ready, diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/SmartLoader.cpp --- a/Framework/SmartLoader.cpp Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/SmartLoader.cpp Tue Sep 18 15:23:21 2018 +0200 @@ -21,6 +21,7 @@ #include "SmartLoader.h" #include "Layers/OrthancFrameLayerSource.h" +#include "Messages/MessageForwarder.h" namespace OrthancStone { @@ -31,41 +32,6 @@ webService_(webService), orthancApiClient_(broker, webService) { -// DeclareHandledMessage(MessageType_LayerSource_GeometryReady); -// DeclareHandledMessage(MessageType_LayerSource_LayerReady); -// DeclareIgnoredMessage(MessageType_LayerSource_GeometryError); -// DeclareIgnoredMessage(MessageType_LayerSource_ContentChanged); -// DeclareIgnoredMessage(MessageType_LayerSource_SliceChanged); - -// DeclareHandledMessage(MessageType_OrthancApi_InternalGetJsonResponseReady); -// DeclareIgnoredMessage(MessageType_OrthancApi_InternalGetJsonResponseError); - } - - void SmartLoader::HandleMessage(IObservable& from, const IMessage& message) - { - switch (message.GetType()) { - case MessageType_LayerSource_GeometryReady: - { - //const OrthancFrameLayerSource* layerSource=dynamic_cast(&from); - // TODO keep track of objects that have been loaded already - }; break; - case MessageType_LayerSource_LayerReady: - { - //const OrthancFrameLayerSource* layerSource=dynamic_cast(&from); - // TODO keep track of objects that have been loaded already - }; break; -// case MessageType_OrthancApi_GetStudyIds_Ready: -// { - -// const OrthancApiClient::GetJsonResponseReadyMessage& msg = dynamic_cast(message); - -// }; break; - default: - VLOG("unhandled message type" << message.GetType()); - } - - // forward messages to its own observers - // TODO TODO TODO IObservable::broker_.EmitMessage(from, IObservable::observers_, message); } ILayerSource* SmartLoader::GetFrame(const std::string& instanceId, unsigned int frame) @@ -76,9 +42,10 @@ // - if currently loading, we need to return an object that will observe the existing LayerSource and forward // the messages to its observables // in both cases, we must be carefull about objects lifecycle !!! - std::auto_ptr layerSource (new OrthancFrameLayerSource(IObserver::broker_, webService_)); + std::auto_ptr layerSource (new OrthancFrameLayerSource(IObserver::broker_, orthancApiClient_)); layerSource->SetImageQuality(imageQuality_); - //layerSource->RegisterObserverCallback(new Callable(*this, &SmartLoader::....)); + layerSource->RegisterObserverCallback(new MessageForwarder(IObserver::broker_, *this)); + layerSource->RegisterObserverCallback(new MessageForwarder(IObserver::broker_, *this)); layerSource->LoadFrame(instanceId, frame); return layerSource.release(); diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/SmartLoader.h --- a/Framework/SmartLoader.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/SmartLoader.h Tue Sep 18 15:23:21 2018 +0200 @@ -40,8 +40,6 @@ public: SmartLoader(MessageBroker& broker, IWebService& webService); // TODO: add maxPreloadStorageSizeInBytes - virtual void HandleMessage(IObservable& from, const IMessage& message); - void PreloadStudy(const std::string studyId); void PreloadSeries(const std::string seriesId); void LoadStudyList(); diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Toolbox/IWebService.h --- a/Framework/Toolbox/IWebService.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Toolbox/IWebService.h Tue Sep 18 15:23:21 2018 +0200 @@ -49,9 +49,9 @@ size_t AnswerSize; Orthanc::IDynamicObject* Payload; NewHttpRequestSuccessMessage(const std::string& uri, - const void* answer, - size_t answerSize, - Orthanc::IDynamicObject* payload) + const void* answer, + size_t answerSize, + Orthanc::IDynamicObject* payload) : BaseMessage(), Uri(uri), Answer(answer), @@ -65,7 +65,7 @@ const std::string& Uri; Orthanc::IDynamicObject* Payload; NewHttpRequestErrorMessage(const std::string& uri, - Orthanc::IDynamicObject* payload) + Orthanc::IDynamicObject* payload) : BaseMessage(), Uri(uri), Payload(payload) @@ -109,8 +109,6 @@ ICallback(MessageBroker& broker) : IObserver(broker) { -// DeclareHandledMessage(MessageType_HttpRequestError); -// DeclareHandledMessage(MessageType_HttpRequestSuccess); } virtual ~ICallback() { @@ -157,22 +155,18 @@ { } - virtual void ScheduleGetRequest(ICallback& callback, - const std::string& uri, - const Headers& headers, - Orthanc::IDynamicObject* payload) = 0; - virtual void GetAsync(const std::string& uri, const Headers& headers, Orthanc::IDynamicObject* payload, MessageHandler* successCallback, MessageHandler* failureCallback) = 0; + virtual void PostAsync(const std::string& uri, + const Headers& headers, + const std::string& body, + Orthanc::IDynamicObject* payload, + MessageHandler* successCallback, + MessageHandler* failureCallback) = 0; - virtual void SchedulePostRequest(ICallback& callback, - const std::string& uri, - const Headers& headers, - const std::string& body, - Orthanc::IDynamicObject* payload) = 0; }; } diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Toolbox/OrthancApiClient.cpp --- a/Framework/Toolbox/OrthancApiClient.cpp Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Toolbox/OrthancApiClient.cpp Tue Sep 18 15:23:21 2018 +0200 @@ -25,193 +25,22 @@ namespace OrthancStone { - struct OrthancApiClient::InternalGetJsonResponseReadyMessage : - public IMessage - { - OrthancApiClient::BaseRequest* request_; - Json::Value response_; - - InternalGetJsonResponseReadyMessage(OrthancApiClient::BaseRequest* request, - const Json::Value& response) - : IMessage(MessageType_OrthancApi_InternalGetJsonResponseReady), - request_(request), - response_(response) - { - } - - }; - - struct OrthancApiClient::InternalGetJsonResponseErrorMessage : - public IMessage - { - OrthancApiClient::BaseRequest* request_; - - InternalGetJsonResponseErrorMessage(OrthancApiClient::BaseRequest* request) - : IMessage(MessageType_OrthancApi_InternalGetJsonResponseError), - request_(request) - { - } - }; - - - // this class handles a single request to the OrthancApiClient. - // Once the response is ready, it will emit a message to the responseObserver - // the responseObserver must handle only that message (and not all messages from the OrthancApiClient) - class OrthancApiClient::BaseRequest: - // public IObserver, - public IObservable, - public Orthanc::IDynamicObject - { - public: - std::string uri_; - OrthancApiClient& orthanc_; - MessageType messageToEmitWhenResponseReady_; - OrthancApiClient::Mode mode_; - - public: - BaseRequest( - OrthancApiClient& orthanc, - IObserver& responseObserver, - const std::string& uri, - MessageType messageToEmitWhenResponseReady, - OrthancApiClient::Mode mode) - : - //IObserver(orthanc.broker_), - IObservable(orthanc.broker_), - uri_(uri), - orthanc_(orthanc), - messageToEmitWhenResponseReady_(messageToEmitWhenResponseReady), - mode_(mode) - { - // this object will emit only a single message, the one the final responseObserver is expecting - DeclareEmittableMessage(messageToEmitWhenResponseReady); - - // // this object is observing the OrthancApi so it must handle all messages - // DeclareHandledMessage(MessageType_OrthancApi_InternalGetJsonResponseReady); - // DeclareIgnoredMessage(MessageType_OrthancApi_InternalGetJsonResponseError); - - //orthanc_.RegisterObserver(*this); - //this->RegisterObserver(responseObserver); - } - virtual ~BaseRequest() {} - - // // mainly maps OrthancApi internal messages to a message that is expected by the responseObserver - // virtual void HandleMessage(IObservable& from, const IMessage& message) - // { - // switch (message.GetType()) - // { - // case MessageType_OrthancApi_InternalGetJsonResponseReady: - // { - // const OrthancApiClient::InternalGetJsonResponseReadyMessage& messageReceived = dynamic_cast(message); - // EmitMessage(OrthancApiClient::GetJsonResponseReadyMessage(messageToEmitWhenResponseReady_, messageReceived.request_->uri_, messageReceived.response_)); - // orthanc_.ReleaseRequest(messageReceived.request_); - // }; break; - // default: - // throw MessageNotDeclaredException(message.GetType()); - // } - // } - - }; - - - class OrthancApiClient::WebCallback : public IWebService::ICallback - { - private: - OrthancApiClient& that_; - - public: - WebCallback(MessageBroker& broker, OrthancApiClient& that) : - IWebService::ICallback(broker), - that_(that) - { - } - - virtual void OnHttpRequestSuccess(const std::string& uri, - const void* answer, - size_t answerSize, - Orthanc::IDynamicObject* payload) - { - OrthancApiClient::BaseRequest* request = dynamic_cast(payload); // the BaseRequests objects belongs to the OrthancApiClient and is deleted in ReleaseRequest when it has been "consumed" - - switch (request->mode_) - { - case OrthancApiClient::Mode_GetJson: - { - Json::Value response; - if (MessagingToolbox::ParseJson(response, answer, answerSize)) - { - request->EmitMessage(OrthancApiClient::GetJsonResponseReadyMessage(request->messageToEmitWhenResponseReady_, request->uri_, response)); - } - else - { - // OrthancApiClient::InternalGetJsonResponseErrorMessage msg(request); - // that_.EmitMessage(msg); - } - }; break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - that_.ReleaseRequest(request); - } - - virtual void OnHttpRequestError(const std::string& uri, - Orthanc::IDynamicObject* payload) - { - OrthancApiClient::BaseRequest* request = dynamic_cast(payload); // the BaseRequests objects belongs to the OrthancApiClient and is deleted in ReleaseRequest when it has been "consumed" - - switch (request->mode_) - { - case OrthancApiClient::Mode_GetJson: - { - // OrthancApiClient::InternalGetJsonResponseErrorMessage msg(request); - // that_.EmitMessage(msg); - // TODO: the request shall send an error message - }; break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - that_.ReleaseRequest(request); - } - }; - OrthancApiClient::OrthancApiClient(MessageBroker &broker, IWebService &orthanc) : IObservable(broker), - orthanc_(orthanc), - webCallback_(new OrthancApiClient::WebCallback(broker, *this)) - { - DeclareEmittableMessage(MessageType_OrthancApi_InternalGetJsonResponseReady); - DeclareEmittableMessage(MessageType_OrthancApi_InternalGetJsonResponseError); - } - - void OrthancApiClient::ScheduleGetJsonRequest(IObserver &responseObserver, const std::string &uri, MessageType messageToEmitWhenResponseReady) + orthanc_(orthanc) { - OrthancApiClient::BaseRequest* request = new OrthancApiClient::BaseRequest(*this, - responseObserver, - uri, - messageToEmitWhenResponseReady, - OrthancApiClient::Mode_GetJson); - orthanc_.ScheduleGetRequest(*webCallback_, uri, IWebService::Headers(), request); - requestsInProgress_.insert(request); - } - - void OrthancApiClient::ReleaseRequest(BaseRequest* request) - { - requestsInProgress_.erase(request); - delete request; } // performs the translation between IWebService messages and OrthancApiClient messages // TODO: handle destruction of this object (with shared_ptr ?::delete_later ???) class HttpResponseToJsonConverter : public IObserver, IObservable { - std::auto_ptr> orthancApiSuccessCallback_; - std::auto_ptr> orthancApiFailureCallback_; + std::auto_ptr> orthancApiSuccessCallback_; + std::auto_ptr> orthancApiFailureCallback_; public: HttpResponseToJsonConverter(MessageBroker& broker, - MessageHandler* orthancApiSuccessCallback, - MessageHandler* orthancApiFailureCallback) + MessageHandler* orthancApiSuccessCallback, + MessageHandler* orthancApiFailureCallback) : IObserver(broker), IObservable(broker), orthancApiSuccessCallback_(orthancApiSuccessCallback), @@ -226,12 +55,12 @@ { if (orthancApiSuccessCallback_.get() != NULL) { - orthancApiSuccessCallback_->Apply(OrthancApiClient::NewGetJsonResponseReadyMessage(message.Uri, response)); + orthancApiSuccessCallback_->Apply(OrthancApiClient::JsonResponseReadyMessage(message.Uri, response, message.Payload)); } } else if (orthancApiFailureCallback_.get() != NULL) { - orthancApiFailureCallback_->Apply(OrthancApiClient::NewHttpErrorMessage(message.Uri)); + orthancApiFailureCallback_->Apply(OrthancApiClient::HttpErrorMessage(message.Uri, message.Payload)); } delete this; // hack untill we find someone to take ownership of this object (https://isocpp.org/wiki/faq/freestore-mgmt#delete-this) @@ -241,7 +70,49 @@ { if (orthancApiFailureCallback_.get() != NULL) { - orthancApiFailureCallback_->Apply(OrthancApiClient::NewHttpErrorMessage(message.Uri)); + orthancApiFailureCallback_->Apply(OrthancApiClient::HttpErrorMessage(message.Uri)); + } + + delete this; // hack untill we find someone to take ownership of this object (https://isocpp.org/wiki/faq/freestore-mgmt#delete-this) + } + }; + + // performs the translation between IWebService messages and OrthancApiClient messages + // TODO: handle destruction of this object (with shared_ptr ?::delete_later ???) + class HttpResponseToBinaryConverter : public IObserver, IObservable + { + std::auto_ptr> orthancApiSuccessCallback_; + std::auto_ptr> orthancApiFailureCallback_; + public: + HttpResponseToBinaryConverter(MessageBroker& broker, + MessageHandler* orthancApiSuccessCallback, + MessageHandler* orthancApiFailureCallback) + : IObserver(broker), + IObservable(broker), + orthancApiSuccessCallback_(orthancApiSuccessCallback), + orthancApiFailureCallback_(orthancApiFailureCallback) + { + } + + void ConvertResponseToBinary(const IWebService::NewHttpRequestSuccessMessage& message) + { + if (orthancApiSuccessCallback_.get() != NULL) + { + orthancApiSuccessCallback_->Apply(OrthancApiClient::BinaryResponseReadyMessage(message.Uri, message.Answer, message.AnswerSize, message.Payload)); + } + else if (orthancApiFailureCallback_.get() != NULL) + { + orthancApiFailureCallback_->Apply(OrthancApiClient::HttpErrorMessage(message.Uri, message.Payload)); + } + + delete this; // hack untill we find someone to take ownership of this object (https://isocpp.org/wiki/faq/freestore-mgmt#delete-this) + } + + void ConvertError(const IWebService::NewHttpRequestErrorMessage& message) + { + if (orthancApiFailureCallback_.get() != NULL) + { + orthancApiFailureCallback_->Apply(OrthancApiClient::HttpErrorMessage(message.Uri)); } delete this; // hack untill we find someone to take ownership of this object (https://isocpp.org/wiki/faq/freestore-mgmt#delete-this) @@ -249,14 +120,41 @@ }; void OrthancApiClient::GetJsonAsync(const std::string& uri, - MessageHandler* successCallback, - MessageHandler* failureCallback) + MessageHandler* successCallback, + MessageHandler* failureCallback, + Orthanc::IDynamicObject* payload) { - HttpResponseToJsonConverter* converter = new HttpResponseToJsonConverter(broker_, successCallback, failureCallback); - orthanc_.GetAsync(uri, IWebService::Headers(), NULL, + HttpResponseToJsonConverter* converter = new HttpResponseToJsonConverter(broker_, successCallback, failureCallback); // it is currently deleting itself after being used + orthanc_.GetAsync(uri, IWebService::Headers(), payload, new Callable(*converter, &HttpResponseToJsonConverter::ConvertResponseToJson), new Callable(*converter, &HttpResponseToJsonConverter::ConvertError)); } + void OrthancApiClient::GetBinaryAsync(const std::string& uri, + const IWebService::Headers& headers, + MessageHandler* successCallback, + MessageHandler* failureCallback, + Orthanc::IDynamicObject* payload) + { + HttpResponseToBinaryConverter* converter = new HttpResponseToBinaryConverter(broker_, successCallback, failureCallback); // it is currently deleting itself after being used + orthanc_.GetAsync(uri, headers, payload, + new Callable(*converter, &HttpResponseToBinaryConverter::ConvertResponseToBinary), + new Callable(*converter, &HttpResponseToBinaryConverter::ConvertError)); + } + + void OrthancApiClient::PostBinaryAsyncExpectJson(const std::string& uri, + const std::string& body, + MessageHandler* successCallback, + MessageHandler* failureCallback, + Orthanc::IDynamicObject* payload) + { + HttpResponseToJsonConverter* converter = new HttpResponseToJsonConverter(broker_, successCallback, failureCallback); // it is currently deleting itself after being used + orthanc_.PostAsync(uri, IWebService::Headers(), body, payload, + new Callable(*converter, &HttpResponseToJsonConverter::ConvertResponseToJson), + new Callable(*converter, &HttpResponseToJsonConverter::ConvertError)); + + } + + } diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Toolbox/OrthancApiClient.h --- a/Framework/Toolbox/OrthancApiClient.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Toolbox/OrthancApiClient.h Tue Sep 18 15:23:21 2018 +0200 @@ -33,55 +33,61 @@ class OrthancApiClient: public IObservable { - protected: - class BaseRequest; - class GetJsonRequest; + public: - struct InternalGetJsonResponseReadyMessage; - struct InternalGetJsonResponseErrorMessage; - - public: - struct GetJsonResponseReadyMessage : public IMessage + struct JsonResponseReadyMessage : public BaseMessage { - Json::Value response_; - std::string uri_; + Json::Value Response; + std::string Uri; + Orthanc::IDynamicObject* Payload; - GetJsonResponseReadyMessage(MessageType messageType, - const std::string& uri, - const Json::Value& response) - : IMessage(messageType), - response_(response), - uri_(uri) + JsonResponseReadyMessage(const std::string& uri, + const Json::Value& response, + Orthanc::IDynamicObject* payload = NULL) + : BaseMessage(), + Response(response), + Uri(uri), + Payload(payload) { } }; - struct NewGetJsonResponseReadyMessage : public BaseMessage + struct HttpErrorMessage : public BaseMessage { - Json::Value response_; - std::string uri_; + std::string Uri; + Orthanc::IDynamicObject* Payload; - NewGetJsonResponseReadyMessage(const std::string& uri, - const Json::Value& response) + HttpErrorMessage(const std::string& uri, + Orthanc::IDynamicObject* payload = NULL) : BaseMessage(), - response_(response), - uri_(uri) + Uri(uri), + Payload(payload) { } }; - struct NewHttpErrorMessage : public BaseMessage + struct BinaryResponseReadyMessage : public BaseMessage { - std::string uri_; + const void* Answer; + size_t AnswerSize; + std::string Uri; + Orthanc::IDynamicObject* Payload; - NewHttpErrorMessage(const std::string& uri) + BinaryResponseReadyMessage(const std::string& uri, + const void* answer, + size_t answerSize, + Orthanc::IDynamicObject* payload = NULL) : BaseMessage(), - uri_(uri) + Answer(answer), + AnswerSize(answerSize), + Uri(uri), + Payload(payload) { } }; + public: enum Mode @@ -91,31 +97,43 @@ protected: IWebService& orthanc_; - class WebCallback; - boost::shared_ptr webCallback_; // This is a PImpl pattern - std::set requestsInProgress_; - - // int ScheduleGetJsonRequest(const std::string& uri); - - void ReleaseRequest(BaseRequest* request); public: OrthancApiClient(MessageBroker& broker, IWebService& orthanc); virtual ~OrthancApiClient() {} - // schedule a GET request expecting a JSON request. - // once the response is ready, it will emit a OrthancApiClient::GetJsonResponseReadyMessage message whose messageType is specified in the call - void ScheduleGetJsonRequest(IObserver& responseObserver, const std::string& uri, MessageType messageToEmitWhenResponseReady); + // schedule a GET request expecting a JSON response. + void GetJsonAsync(const std::string& uri, + MessageHandler* successCallback, + MessageHandler* failureCallback = NULL, + Orthanc::IDynamicObject* payload = NULL); - void ScheduleGetStudyIds(IObserver& responseObserver) {ScheduleGetJsonRequest(responseObserver, "/studies", MessageType_OrthancApi_GetStudyIds_Ready);} - void ScheduleGetStudy(IObserver& responseObserver, const std::string& studyId) {ScheduleGetJsonRequest(responseObserver, "/studies/" + studyId, MessageType_OrthancApi_GetStudy_Ready);} - void ScheduleGetSeries(IObserver& responseObserver, const std::string& seriesId) {ScheduleGetJsonRequest(responseObserver, "/series/" + seriesId, MessageType_OrthancApi_GetSeries_Ready);} + // schedule a GET request expecting a binary response. + void GetBinaryAsync(const std::string& uri, + const std::string& contentType, + MessageHandler* successCallback, + MessageHandler* failureCallback = NULL, + Orthanc::IDynamicObject* payload = NULL) + { + IWebService::Headers headers; + headers["Accept"] = contentType; + GetBinaryAsync(uri, headers, successCallback, failureCallback, payload); + } - void GetJsonAsync(const std::string& uri, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL); + // schedule a GET request expecting a binary response. + void GetBinaryAsync(const std::string& uri, + const IWebService::Headers& headers, + MessageHandler* successCallback, + MessageHandler* failureCallback = NULL, + Orthanc::IDynamicObject* payload = NULL); + // schedule a POST request expecting a JSON response. + void PostBinaryAsyncExpectJson(const std::string& uri, + const std::string& body, + MessageHandler* successCallback, + MessageHandler* failureCallback = NULL, + Orthanc::IDynamicObject* payload = NULL); }; diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Toolbox/OrthancSlicesLoader.cpp --- a/Framework/Toolbox/OrthancSlicesLoader.cpp Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Toolbox/OrthancSlicesLoader.cpp Tue Sep 18 15:23:21 2018 +0200 @@ -79,32 +79,32 @@ const Slice* slice_; std::string instanceId_; SliceImageQuality quality_; - + Operation(Mode mode) : mode_(mode) { } - + public: Mode GetMode() const { return mode_; } - + SliceImageQuality GetQuality() const { assert(mode_ == Mode_LoadImage || mode_ == Mode_LoadRawImage); return quality_; } - + unsigned int GetSliceIndex() const { assert(mode_ == Mode_LoadImage || mode_ == Mode_LoadRawImage); return sliceIndex_; } - + const Slice& GetSlice() const { assert(mode_ == Mode_LoadImage || @@ -112,7 +112,7 @@ assert(slice_ != NULL); return *slice_; } - + unsigned int GetFrame() const { assert(mode_ == Mode_FrameGeometry); @@ -126,18 +126,13 @@ return instanceId_; } - static Operation* DownloadSeriesGeometry() - { - return new Operation(Mode_SeriesGeometry); - } - static Operation* DownloadInstanceGeometry(const std::string& instanceId) { std::auto_ptr operation(new Operation(Mode_InstanceGeometry)); operation->instanceId_ = instanceId; return operation.release(); } - + static Operation* DownloadFrameGeometry(const std::string& instanceId, unsigned int frame) { @@ -146,7 +141,7 @@ operation->frame_ = frame; return operation.release(); } - + static Operation* DownloadSliceImage(unsigned int sliceIndex, const Slice& slice, SliceImageQuality quality) @@ -157,7 +152,7 @@ tmp->quality_ = quality; return tmp.release(); } - + static Operation* DownloadSliceRawImage(unsigned int sliceIndex, const Slice& slice) { @@ -177,107 +172,6 @@ }; - - class OrthancSlicesLoader::WebCallback : public IWebService::ICallback - { - private: - OrthancSlicesLoader& that_; - - public: - WebCallback(MessageBroker& broker, OrthancSlicesLoader& that) : - IWebService::ICallback(broker), - that_(that) - { - } - - virtual void OnHttpRequestSuccess(const std::string& uri, - const void* answer, - size_t answerSize, - Orthanc::IDynamicObject* payload) - { - std::auto_ptr operation(dynamic_cast(payload)); - - switch (operation->GetMode()) - { - case Mode_SeriesGeometry: - that_.ParseSeriesGeometry(answer, answerSize); - break; - - case Mode_InstanceGeometry: - that_.ParseInstanceGeometry(operation->GetInstanceId(), answer, answerSize); - break; - - case Mode_FrameGeometry: - that_.ParseFrameGeometry(operation->GetInstanceId(), - operation->GetFrame(), answer, answerSize); - break; - - case Mode_LoadImage: - switch (operation->GetQuality()) - { - case SliceImageQuality_FullPng: - that_.ParseSliceImagePng(*operation, answer, answerSize); - break; - case SliceImageQuality_FullPam: - that_.ParseSliceImagePam(*operation, answer, answerSize); - break; - - case SliceImageQuality_Jpeg50: - case SliceImageQuality_Jpeg90: - case SliceImageQuality_Jpeg95: - that_.ParseSliceImageJpeg(*operation, answer, answerSize); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - break; - - case Mode_LoadRawImage: - that_.ParseSliceRawImage(*operation, answer, answerSize); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - virtual void OnHttpRequestError(const std::string& uri, - Orthanc::IDynamicObject* payload) - { - std::auto_ptr operation(dynamic_cast(payload)); - LOG(ERROR) << "Cannot download " << uri; - - switch (operation->GetMode()) - { - case Mode_FrameGeometry: - case Mode_SeriesGeometry: - that_.EmitMessage(SliceGeometryErrorMessage(that_)); - that_.state_ = State_Error; - break; - - case Mode_LoadImage: - { - OrthancSlicesLoader::SliceImageErrorMessage msg(operation->GetSliceIndex(), - operation->GetSlice(), - operation->GetQuality()); - that_.EmitMessage(msg); - }; break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - }; - -// void OrthancSlicesLoader::HandleMessage(IObservable& from, const IMessage& message) -// { -// // forward messages to its own observers -// IObservable::broker_.EmitMessage(from, IObservable::observers_, message); -// } - - void OrthancSlicesLoader::NotifySliceImageSuccess(const Operation& operation, std::auto_ptr& image) { @@ -330,18 +224,21 @@ } } - - void OrthancSlicesLoader::ParseSeriesGeometry(const void* answer, - size_t size) + void OrthancSlicesLoader::OnGeometryError(const OrthancApiClient::HttpErrorMessage& message) + { + EmitMessage(SliceGeometryErrorMessage(*this)); + state_ = State_Error; + } + + void OrthancSlicesLoader::OnSliceImageError(const OrthancApiClient::HttpErrorMessage& message) { - Json::Value series; - if (!MessagingToolbox::ParseJson(series, answer, size) || - series.type() != Json::objectValue) - { - EmitMessage(SliceGeometryErrorMessage(*this)); - return; - } - + NotifySliceImageError(dynamic_cast(*(message.Payload))); + state_ = State_Error; + } + + void OrthancSlicesLoader::ParseSeriesGeometry(const OrthancApiClient::JsonResponseReadyMessage& message) + { + Json::Value series = message.Response; Json::Value::Members instances = series.getMemberNames(); slices_.Reserve(instances.size()); @@ -376,19 +273,11 @@ SortAndFinalizeSlices(); } - - void OrthancSlicesLoader::ParseInstanceGeometry(const std::string& instanceId, - const void* answer, - size_t size) + void OrthancSlicesLoader::ParseInstanceGeometry(const OrthancApiClient::JsonResponseReadyMessage& message) { - Json::Value tags; - if (!MessagingToolbox::ParseJson(tags, answer, size) || - tags.type() != Json::objectValue) - { - EmitMessage(SliceGeometryErrorMessage(*this)); - return; - } - + Json::Value tags = message.Response; + const std::string& instanceId = dynamic_cast(message.Payload)->GetInstanceId(); + OrthancPlugins::FullOrthancDataset dataset(tags); Orthanc::DicomMap dicom; @@ -421,19 +310,12 @@ } - void OrthancSlicesLoader::ParseFrameGeometry(const std::string& instanceId, - unsigned int frame, - const void* answer, - size_t size) + void OrthancSlicesLoader::ParseFrameGeometry(const OrthancApiClient::JsonResponseReadyMessage& message) { - Json::Value tags; - if (!MessagingToolbox::ParseJson(tags, answer, size) || - tags.type() != Json::objectValue) - { - EmitMessage(SliceGeometryErrorMessage(*this)); - return; - } - + Json::Value tags = message.Response; + const std::string& instanceId = dynamic_cast(message.Payload)->GetInstanceId(); + unsigned int frame = dynamic_cast(message.Payload)->GetFrame(); + OrthancPlugins::FullOrthancDataset dataset(tags); state_ = State_GeometryReady; @@ -446,7 +328,7 @@ { LOG(INFO) << "Loaded instance geometry " << instanceId; slices_.AddSlice(slice.release()); - EmitMessage(SliceGeometryErrorMessage(*this)); + EmitMessage(SliceGeometryReadyMessage(*this)); } else { @@ -456,16 +338,15 @@ } - void OrthancSlicesLoader::ParseSliceImagePng(const Operation& operation, - const void* answer, - size_t size) + void OrthancSlicesLoader::ParseSliceImagePng(const OrthancApiClient::BinaryResponseReadyMessage& message) { + const Operation& operation = dynamic_cast(*message.Payload); std::auto_ptr image; try { image.reset(new Orthanc::PngReader); - dynamic_cast(*image).ReadFromMemory(answer, size); + dynamic_cast(*image).ReadFromMemory(message.Answer, message.AnswerSize); } catch (Orthanc::OrthancException&) { @@ -497,16 +378,15 @@ NotifySliceImageSuccess(operation, image); } - void OrthancSlicesLoader::ParseSliceImagePam(const Operation& operation, - const void* answer, - size_t size) + void OrthancSlicesLoader::ParseSliceImagePam(const OrthancApiClient::BinaryResponseReadyMessage& message) { + const Operation& operation = dynamic_cast(*message.Payload); std::auto_ptr image; try { image.reset(new Orthanc::PamReader); - dynamic_cast(*image).ReadFromMemory(std::string(reinterpret_cast(answer), size)); + dynamic_cast(*image).ReadFromMemory(std::string(reinterpret_cast(message.Answer), message.AnswerSize)); } catch (Orthanc::OrthancException&) { @@ -539,13 +419,12 @@ } - void OrthancSlicesLoader::ParseSliceImageJpeg(const Operation& operation, - const void* answer, - size_t size) + void OrthancSlicesLoader::ParseSliceImageJpeg(const OrthancApiClient::JsonResponseReadyMessage& message) { - Json::Value encoded; - if (!MessagingToolbox::ParseJson(encoded, answer, size) || - encoded.type() != Json::objectValue || + const Operation& operation = dynamic_cast(*message.Payload); + + Json::Value encoded = message.Response; + if (encoded.type() != Json::objectValue || !encoded.isMember("Orthanc") || encoded["Orthanc"].type() != Json::objectValue) { @@ -714,14 +593,13 @@ } }; - void OrthancSlicesLoader::ParseSliceRawImage(const Operation& operation, - const void* answer, - size_t size) + void OrthancSlicesLoader::ParseSliceRawImage(const OrthancApiClient::BinaryResponseReadyMessage& message) { + const Operation& operation = dynamic_cast(*message.Payload); Orthanc::GzipCompressor compressor; std::string raw; - compressor.Uncompress(raw, answer, size); + compressor.Uncompress(raw, message.Answer, message.AnswerSize); const Orthanc::DicomImageInformation& info = operation.GetSlice().GetImageInformation(); @@ -776,18 +654,12 @@ OrthancSlicesLoader::OrthancSlicesLoader(MessageBroker& broker, - //ISliceLoaderObserver& callback, - IWebService& orthanc) : + OrthancApiClient& orthanc) : IObservable(broker), - webCallback_(new WebCallback(broker, *this)), - //userCallback_(callback), + IObserver(broker), orthanc_(orthanc), state_(State_Initialization) { - DeclareEmittableMessage(MessageType_SliceLoader_GeometryReady); - DeclareEmittableMessage(MessageType_SliceLoader_GeometryError); - DeclareEmittableMessage(MessageType_SliceLoader_ImageError); - DeclareEmittableMessage(MessageType_SliceLoader_ImageReady); } @@ -800,12 +672,13 @@ else { state_ = State_LoadingGeometry; - std::string uri = "/series/" + seriesId + "/instances-tags"; - orthanc_.ScheduleGetRequest(*webCallback_, uri, IWebService::Headers(), Operation::DownloadSeriesGeometry()); + orthanc_.GetJsonAsync("/series/" + seriesId + "/instances-tags", + new Callable(*this, &OrthancSlicesLoader::ParseSeriesGeometry), + new Callable(*this, &OrthancSlicesLoader::OnGeometryError), + NULL); } } - void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId) { if (state_ != State_Initialization) @@ -818,9 +691,10 @@ // Tag "3004-000c" is "Grid Frame Offset Vector", which is // mandatory to read RT DOSE, but is too long to be returned by default - std::string uri = "/instances/" + instanceId + "/tags?ignore-length=3004-000c"; - orthanc_.ScheduleGetRequest - (*webCallback_, uri, IWebService::Headers(), Operation::DownloadInstanceGeometry(instanceId)); + orthanc_.GetJsonAsync("/instances/" + instanceId + "/tags?ignore-length=3004-000c", + new Callable(*this, &OrthancSlicesLoader::ParseInstanceGeometry), + new Callable(*this, &OrthancSlicesLoader::OnGeometryError), + Operation::DownloadInstanceGeometry(instanceId)); } } @@ -835,9 +709,11 @@ else { state_ = State_LoadingGeometry; - std::string uri = "/instances/" + instanceId + "/tags"; - orthanc_.ScheduleGetRequest - (*webCallback_, uri, IWebService::Headers(), Operation::DownloadFrameGeometry(instanceId, frame)); + + orthanc_.GetJsonAsync("/instances/" + instanceId + "/tags", + new Callable(*this, &OrthancSlicesLoader::ParseFrameGeometry), + new Callable(*this, &OrthancSlicesLoader::OnGeometryError), + Operation::DownloadFrameGeometry(instanceId, frame)); } } @@ -906,10 +782,10 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - IWebService::Headers headers; - headers["Accept"] = "image/png"; - orthanc_.ScheduleGetRequest(*webCallback_, uri, IWebService::Headers(), - Operation::DownloadSliceImage(index, slice, SliceImageQuality_FullPng)); + orthanc_.GetBinaryAsync(uri, "image/png", + new Callable(*this, &OrthancSlicesLoader::ParseSliceImagePng), + new Callable(*this, &OrthancSlicesLoader::OnSliceImageError), + Operation::DownloadSliceImage(index, slice, SliceImageQuality_FullPng)); } void OrthancSlicesLoader::ScheduleSliceImagePam(const Slice& slice, @@ -936,10 +812,10 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - IWebService::Headers headers; - headers["Accept"] = "image/x-portable-arbitrarymap"; - orthanc_.ScheduleGetRequest(*webCallback_, uri, headers, - Operation::DownloadSliceImage(index, slice, SliceImageQuality_FullPam)); + orthanc_.GetBinaryAsync(uri, "image/x-portable-arbitrarymap", + new Callable(*this, &OrthancSlicesLoader::ParseSliceImagePam), + new Callable(*this, &OrthancSlicesLoader::OnSliceImageError), + Operation::DownloadSliceImage(index, slice, SliceImageQuality_FullPam)); } @@ -974,8 +850,10 @@ "-" + slice.GetOrthancInstanceId() + "_" + boost::lexical_cast(slice.GetFrame())); - orthanc_.ScheduleGetRequest(*webCallback_, uri, IWebService::Headers(), - Operation::DownloadSliceImage(index, slice, quality)); + orthanc_.GetJsonAsync(uri, + new Callable(*this, &OrthancSlicesLoader::ParseSliceImageJpeg), + new Callable(*this, &OrthancSlicesLoader::OnSliceImageError), + Operation::DownloadSliceImage(index, slice, quality)); } @@ -1008,8 +886,10 @@ { std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + boost::lexical_cast(slice.GetFrame()) + "/raw.gz"); - orthanc_.ScheduleGetRequest(*webCallback_, uri, IWebService::Headers(), - Operation::DownloadSliceRawImage(index, slice)); + orthanc_.GetBinaryAsync(uri, IWebService::Headers(), + new Callable(*this, &OrthancSlicesLoader::ParseSliceRawImage), + new Callable(*this, &OrthancSlicesLoader::OnSliceImageError), + Operation::DownloadSliceRawImage(index, slice)); } } } diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Toolbox/OrthancSlicesLoader.h --- a/Framework/Toolbox/OrthancSlicesLoader.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Toolbox/OrthancSlicesLoader.h Tue Sep 18 15:23:21 2018 +0200 @@ -26,10 +26,11 @@ #include "../StoneEnumerations.h" #include "../Messages/IObservable.h" #include +#include "OrthancApiClient.h" namespace OrthancStone { - class OrthancSlicesLoader : public IObservable + class OrthancSlicesLoader : public IObservable, public IObserver { public: @@ -93,11 +94,8 @@ }; class Operation; - class WebCallback; - boost::shared_ptr webCallback_; // This is a PImpl pattern - - IWebService& orthanc_; + OrthancApiClient& orthanc_; State state_; SlicesSorter slices_; @@ -105,34 +103,23 @@ std::auto_ptr& image); void NotifySliceImageError(const Operation& operation); - - void ParseSeriesGeometry(const void* answer, - size_t size); + + void OnGeometryError(const OrthancApiClient::HttpErrorMessage& message); + void OnSliceImageError(const OrthancApiClient::HttpErrorMessage& message); - void ParseInstanceGeometry(const std::string& instanceId, - const void* answer, - size_t size); + void ParseSeriesGeometry(const OrthancApiClient::JsonResponseReadyMessage& message); - void ParseFrameGeometry(const std::string& instanceId, - unsigned int frame, - const void* answer, - size_t size); + void ParseInstanceGeometry(const OrthancApiClient::JsonResponseReadyMessage& message); - void ParseSliceImagePng(const Operation& operation, - const void* answer, - size_t size); + void ParseFrameGeometry(const OrthancApiClient::JsonResponseReadyMessage& message); - void ParseSliceImagePam(const Operation& operation, - const void* answer, - size_t size); + void ParseSliceImagePng(const OrthancApiClient::BinaryResponseReadyMessage& message); - void ParseSliceImageJpeg(const Operation& operation, - const void* answer, - size_t size); + void ParseSliceImagePam(const OrthancApiClient::BinaryResponseReadyMessage& message); - void ParseSliceRawImage(const Operation& operation, - const void* answer, - size_t size); + void ParseSliceImageJpeg(const OrthancApiClient::JsonResponseReadyMessage& message); + + void ParseSliceRawImage(const OrthancApiClient::BinaryResponseReadyMessage& message); void ScheduleSliceImagePng(const Slice& slice, size_t index); @@ -149,7 +136,7 @@ public: OrthancSlicesLoader(MessageBroker& broker, //ISliceLoaderObserver& callback, - IWebService& orthanc); + OrthancApiClient& orthancApi); void ScheduleLoadSeries(const std::string& seriesId); @@ -170,6 +157,6 @@ void ScheduleLoadSliceImage(size_t index, SliceImageQuality requestedQuality); -// virtual void HandleMessage(IObservable& from, const IMessage& message); + }; } diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Volumes/StructureSetLoader.cpp --- a/Framework/Volumes/StructureSetLoader.cpp Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Volumes/StructureSetLoader.cpp Tue Sep 18 15:23:21 2018 +0200 @@ -27,131 +27,63 @@ namespace OrthancStone { - class StructureSetLoader::Operation : public Orthanc::IDynamicObject - { - public: - enum Type - { - Type_LoadStructureSet, - Type_LookupSopInstanceUid, - Type_LoadReferencedSlice - }; - - private: - Type type_; - std::string value_; - - public: - Operation(Type type, - const std::string& value) : - type_(type), - value_(value) - { - } - - Type GetType() const - { - return type_; - } - - const std::string& GetIdentifier() const - { - return value_; - } - }; - - - void StructureSetLoader::OnHttpRequestError(const std::string& uri, - Orthanc::IDynamicObject* payload) - { - // TODO - } - - void StructureSetLoader::OnHttpRequestSuccess(const std::string& uri, - const void* answer, - size_t answerSize, - Orthanc::IDynamicObject* payload) - { - std::auto_ptr op(dynamic_cast(payload)); - - switch (op->GetType()) - { - case Operation::Type_LoadStructureSet: - { - OrthancPlugins::FullOrthancDataset dataset(answer, answerSize); - structureSet_.reset(new DicomStructureSet(dataset)); - - std::set instances; - structureSet_->GetReferencedInstances(instances); - - for (std::set::const_iterator it = instances.begin(); - it != instances.end(); ++it) - { - orthanc_.SchedulePostRequest(*this, "/tools/lookup", IWebService::Headers(), *it, - new Operation(Operation::Type_LookupSopInstanceUid, *it)); - } - - VolumeLoaderBase::NotifyGeometryReady(); - - break; - } - - case Operation::Type_LookupSopInstanceUid: - { - Json::Value lookup; - - if (MessagingToolbox::ParseJson(lookup, answer, answerSize)) - { - if (lookup.type() != Json::arrayValue || - lookup.size() != 1 || - !lookup[0].isMember("Type") || - !lookup[0].isMember("Path") || - lookup[0]["Type"].type() != Json::stringValue || - lookup[0]["ID"].type() != Json::stringValue || - lookup[0]["Type"].asString() != "Instance") - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); - } - - const std::string& instance = lookup[0]["ID"].asString(); - orthanc_.ScheduleGetRequest(*this, "/instances/" + instance + "/tags", IWebService::Headers(), - new Operation(Operation::Type_LoadReferencedSlice, instance)); - } - else - { - // TODO - } - - break; - } - - case Operation::Type_LoadReferencedSlice: - { - OrthancPlugins::FullOrthancDataset dataset(answer, answerSize); - - Orthanc::DicomMap slice; - MessagingToolbox::ConvertDataset(slice, dataset); - structureSet_->AddReferencedSlice(slice); - - VolumeLoaderBase::NotifyContentChange(); - - break; - } - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - - StructureSetLoader::StructureSetLoader(MessageBroker& broker, IWebService& orthanc) : - IWebService::ICallback(broker), + StructureSetLoader::StructureSetLoader(MessageBroker& broker, OrthancApiClient& orthanc) : + OrthancStone::IObserver(broker), orthanc_(orthanc) { } + void StructureSetLoader::OnReferencedSliceLoaded(const OrthancApiClient::JsonResponseReadyMessage& message) + { + OrthancPlugins::FullOrthancDataset dataset(message.Response); + + Orthanc::DicomMap slice; + MessagingToolbox::ConvertDataset(slice, dataset); + structureSet_->AddReferencedSlice(slice); + + VolumeLoaderBase::NotifyContentChange(); + } + + void StructureSetLoader::OnStructureSetLoaded(const OrthancApiClient::JsonResponseReadyMessage& message) + { + OrthancPlugins::FullOrthancDataset dataset(message.Response); + structureSet_.reset(new DicomStructureSet(dataset)); + + std::set instances; + structureSet_->GetReferencedInstances(instances); + + for (std::set::const_iterator it = instances.begin(); + it != instances.end(); ++it) + { + orthanc_.PostBinaryAsyncExpectJson("/tools/lookup", *it, + new Callable(*this, &StructureSetLoader::OnLookupCompleted)); + } + + VolumeLoaderBase::NotifyGeometryReady(); + } + + void StructureSetLoader::OnLookupCompleted(const OrthancApiClient::JsonResponseReadyMessage& message) + { + Json::Value lookup = message.Response; + + if (lookup.type() != Json::arrayValue || + lookup.size() != 1 || + !lookup[0].isMember("Type") || + !lookup[0].isMember("Path") || + lookup[0]["Type"].type() != Json::stringValue || + lookup[0]["ID"].type() != Json::stringValue || + lookup[0]["Type"].asString() != "Instance") + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + const std::string& instance = lookup[0]["ID"].asString(); + orthanc_.GetJsonAsync("/instances/" + instance + "/tags", + new Callable(*this, &StructureSetLoader::OnReferencedSliceLoaded)); + } + void StructureSetLoader::ScheduleLoadInstance(const std::string& instance) { if (structureSet_.get() != NULL) @@ -160,8 +92,8 @@ } else { - const std::string uri = "/instances/" + instance + "/tags?ignore-length=3006-0050"; - orthanc_.ScheduleGetRequest(*this, uri, IWebService::Headers(), new Operation(Operation::Type_LoadStructureSet, instance)); + orthanc_.GetJsonAsync("/instances/" + instance + "/tags?ignore-length=3006-0050", + new Callable(*this, &StructureSetLoader::OnStructureSetLoaded)); } } diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Volumes/StructureSetLoader.h --- a/Framework/Volumes/StructureSetLoader.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Volumes/StructureSetLoader.h Tue Sep 18 15:23:21 2018 +0200 @@ -22,31 +22,22 @@ #pragma once #include "../Toolbox/DicomStructureSet.h" -#include "../Toolbox/IWebService.h" +#include "../Toolbox/OrthancApiClient.h" #include "VolumeLoaderBase.h" namespace OrthancStone { class StructureSetLoader : public VolumeLoaderBase, - private IWebService::ICallback + public OrthancStone::IObserver { private: - class Operation; - - virtual void OnHttpRequestError(const std::string& uri, - Orthanc::IDynamicObject* payload); - virtual void OnHttpRequestSuccess(const std::string& uri, - const void* answer, - size_t answerSize, - Orthanc::IDynamicObject* payload); - - IWebService& orthanc_; + OrthancApiClient& orthanc_; std::auto_ptr structureSet_; public: - StructureSetLoader(MessageBroker& broker, IWebService& orthanc); + StructureSetLoader(MessageBroker& broker, OrthancApiClient& orthanc); void ScheduleLoadInstance(const std::string& instance); @@ -56,5 +47,12 @@ } DicomStructureSet& GetStructureSet(); + + protected: + void OnReferencedSliceLoaded(const OrthancApiClient::JsonResponseReadyMessage& message); + + void OnStructureSetLoaded(const OrthancApiClient::JsonResponseReadyMessage& message); + + void OnLookupCompleted(const OrthancApiClient::JsonResponseReadyMessage& message); }; } diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Widgets/LayerWidget.cpp --- a/Framework/Widgets/LayerWidget.cpp Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Widgets/LayerWidget.cpp Tue Sep 18 15:23:21 2018 +0200 @@ -386,6 +386,15 @@ } } + void LayerWidget::ObserveLayer(ILayerSource& layer) + { + layer.RegisterObserverCallback(new Callable(*this, &LayerWidget::OnGeometryReady)); + // currently ignore errors layer->RegisterObserverCallback(new Callable(*this, &LayerWidget::...)); + layer.RegisterObserverCallback(new Callable(*this, &LayerWidget::OnSliceChanged)); + layer.RegisterObserverCallback(new Callable(*this, &LayerWidget::OnContentChanged)); + layer.RegisterObserverCallback(new Callable(*this, &LayerWidget::OnLayerReady)); + } + size_t LayerWidget::AddLayer(ILayerSource* layer) // Takes ownership { @@ -400,7 +409,8 @@ layersIndex_[layer] = index; ResetPendingScene(); -// layer->RegisterObserver(*this); + + ObserveLayer(*layer); ResetChangedLayers(); @@ -424,7 +434,8 @@ layersIndex_[layer] = index; ResetPendingScene(); -// layer->RegisterObserver(*this); + + ObserveLayer(*layer); InvalidateLayer(index); } @@ -491,38 +502,10 @@ } } - void LayerWidget::HandleMessage(IObservable& from, const IMessage& message) - { - switch (message.GetType()) { - case MessageType_LayerSource_GeometryReady: - OnGeometryReady(dynamic_cast(from)); - break; - case MessageType_LayerSource_GeometryError: - LOG(ERROR) << "Cannot get geometry"; - break; - case MessageType_LayerSource_ContentChanged: - OnContentChanged(dynamic_cast(from)); - break; - case MessageType_LayerSource_SliceChanged: - OnSliceChanged(dynamic_cast(from), dynamic_cast(message).slice_); - break; - case MessageType_LayerSource_LayerReady: - { - const ILayerSource::LayerReadyMessage& layerReadyMessage = dynamic_cast(message); - OnLayerReady(layerReadyMessage.layer_, - dynamic_cast(from), - layerReadyMessage.slice_, - layerReadyMessage.isError_); - }; break; - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - } - - void LayerWidget::OnGeometryReady(const ILayerSource& source) + void LayerWidget::OnGeometryReady(const ILayerSource::GeometryReadyMessage& message) { size_t i; - if (LookupLayer(i, source)) + if (LookupLayer(i, message.origin_)) { LOG(INFO) << ": Geometry ready for layer " << i << " in " << GetName(); @@ -558,23 +541,22 @@ } - void LayerWidget::OnContentChanged(const ILayerSource& source) + void LayerWidget::OnContentChanged(const ILayerSource::ContentChangedMessage& message) { size_t index; - if (LookupLayer(index, source)) + if (LookupLayer(index, message.origin_)) { InvalidateLayer(index); } } - void LayerWidget::OnSliceChanged(const ILayerSource& source, - const Slice& slice) + void LayerWidget::OnSliceChanged(const ILayerSource::SliceChangedMessage& message) { - if (slice.ContainsPlane(slice_)) + if (message.slice_.ContainsPlane(slice_)) { size_t index; - if (LookupLayer(index, source)) + if (LookupLayer(index, message.origin_)) { InvalidateLayer(index); } @@ -582,15 +564,12 @@ } - void LayerWidget::OnLayerReady(std::auto_ptr& renderer, - const ILayerSource& source, - const CoordinateSystem3D& slice, - bool isError) + void LayerWidget::OnLayerReady(const ILayerSource::LayerReadyMessage& message) { size_t index; - if (LookupLayer(index, source)) + if (LookupLayer(index, message.origin_)) { - if (isError) + if (message.isError_) { LOG(ERROR) << "Using error renderer on layer " << index; } @@ -599,11 +578,11 @@ LOG(INFO) << "Renderer ready for layer " << index; } - if (renderer.get() != NULL) + if (message.renderer_.get() != NULL) { - UpdateLayer(index, renderer.release(), slice); + UpdateLayer(index, message.renderer_.release(), message.slice_); } - else if (isError) + else if (message.isError_) { // TODO //UpdateLayer(index, new SliceOutlineRenderer(slice), slice); diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/Widgets/LayerWidget.h --- a/Framework/Widgets/LayerWidget.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/Widgets/LayerWidget.h Tue Sep 18 15:23:21 2018 +0200 @@ -59,26 +59,21 @@ void GetLayerExtent(Extent2D& extent, ILayerSource& source) const; - void OnGeometryReady(const ILayerSource& source); + void OnGeometryReady(const ILayerSource::GeometryReadyMessage& message); - virtual void OnContentChanged(const ILayerSource& source); + virtual void OnContentChanged(const ILayerSource::ContentChangedMessage& message); - virtual void OnSliceChanged(const ILayerSource& source, - const Slice& slice); + virtual void OnSliceChanged(const ILayerSource::SliceChangedMessage& message); - virtual void OnLayerReady(std::auto_ptr& renderer, - const ILayerSource& source, - const CoordinateSystem3D& slice, - bool isError); + virtual void OnLayerReady(const ILayerSource::LayerReadyMessage& message); + void ObserveLayer(ILayerSource& source); void ResetChangedLayers(); public: LayerWidget(MessageBroker& broker, const std::string& name); - virtual void HandleMessage(IObservable& from, const IMessage& message); - virtual Extent2D GetSceneExtent(); protected: diff -r 3897f9f28cfa -r b4abaeb783b1 Framework/dev.h --- a/Framework/dev.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Framework/dev.h Tue Sep 18 15:23:21 2018 +0200 @@ -232,7 +232,7 @@ public: OrthancVolumeImage(MessageBroker& broker, - IWebService& orthanc, + OrthancApiClient& orthanc, bool computeRange) : OrthancStone::IObserver(broker), loader_(broker, orthanc), diff -r 3897f9f28cfa -r b4abaeb783b1 Platforms/Generic/OracleWebService.h --- a/Platforms/Generic/OracleWebService.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Platforms/Generic/OracleWebService.h Tue Sep 18 15:23:21 2018 +0200 @@ -51,30 +51,23 @@ { } - virtual void ScheduleGetRequest(ICallback& callback, - const std::string& uri, - const Headers& headers, - Orthanc::IDynamicObject* payload) - { - oracle_.Submit(new WebServiceGetCommand(broker_, callback, parameters_, uri, headers, payload, context_)); - } - virtual void GetAsync(const std::string& uri, const Headers& headers, - Orthanc::IDynamicObject* payload, + Orthanc::IDynamicObject* payload, // takes ownership MessageHandler* successCallback, // takes ownership MessageHandler* failureCallback = NULL)// takes ownership { - oracle_.Submit(new NewWebServiceGetCommand(broker_, successCallback, failureCallback, parameters_, uri, headers, payload, context_)); + oracle_.Submit(new WebServiceGetCommand(broker_, successCallback, failureCallback, parameters_, uri, headers, payload, context_)); } - virtual void SchedulePostRequest(ICallback& callback, - const std::string& uri, - const Headers& headers, - const std::string& body, - Orthanc::IDynamicObject* payload) + virtual void PostAsync(const std::string& uri, + const Headers& headers, + const std::string& body, + Orthanc::IDynamicObject* payload, // takes ownership + MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback) // takes ownership { - oracle_.Submit(new WebServicePostCommand(broker_, callback, parameters_, uri, headers, body, payload, context_)); + oracle_.Submit(new WebServicePostCommand(broker_, successCallback, failureCallback, parameters_, uri, headers, body, payload, context_)); } void Start() diff -r 3897f9f28cfa -r b4abaeb783b1 Platforms/Generic/WebServiceCommandBase.cpp --- a/Platforms/Generic/WebServiceCommandBase.cpp Fri Sep 14 16:44:01 2018 +0200 +++ b/Platforms/Generic/WebServiceCommandBase.cpp Tue Sep 18 15:23:21 2018 +0200 @@ -26,45 +26,8 @@ namespace OrthancStone { WebServiceCommandBase::WebServiceCommandBase(MessageBroker& broker, - IWebService::ICallback& callback, - const Orthanc::WebServiceParameters& parameters, - const std::string& uri, - const IWebService::Headers& headers, - Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context) : - IObservable(broker), - callback_(callback), - parameters_(parameters), - uri_(uri), - headers_(headers), - payload_(payload), - context_(context) - { - DeclareEmittableMessage(MessageType_HttpRequestError); - DeclareEmittableMessage(MessageType_HttpRequestSuccess); - // TODO ? RegisterObserver(callback); - } - - - void WebServiceCommandBase::Commit() - { - NativeStoneApplicationContext::GlobalMutexLocker lock(context_); // we want to make sure that, i.e, the UpdateThread is not triggered while we are updating the "model" with the result of a WebServiceCommand - - if (success_) - { - IWebService::ICallback::HttpRequestSuccessMessage message(uri_, answer_.c_str(), answer_.size(), payload_.release()); - EmitMessage(message); - } - else - { - IWebService::ICallback::HttpRequestErrorMessage message(uri_, payload_.release()); - EmitMessage(message); - } - } - - NewWebServiceCommandBase::NewWebServiceCommandBase(MessageBroker& broker, - MessageHandler* successCallback, - MessageHandler* failureCallback, + MessageHandler* successCallback, + MessageHandler* failureCallback, const Orthanc::WebServiceParameters& parameters, const std::string& uri, const IWebService::Headers& headers, @@ -82,7 +45,7 @@ } - void NewWebServiceCommandBase::Commit() + void WebServiceCommandBase::Commit() { NativeStoneApplicationContext::GlobalMutexLocker lock(context_); // we want to make sure that, i.e, the UpdateThread is not triggered while we are updating the "model" with the result of a WebServiceCommand diff -r 3897f9f28cfa -r b4abaeb783b1 Platforms/Generic/WebServiceCommandBase.h --- a/Platforms/Generic/WebServiceCommandBase.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Platforms/Generic/WebServiceCommandBase.h Tue Sep 18 15:23:21 2018 +0200 @@ -37,32 +37,6 @@ class WebServiceCommandBase : public IOracleCommand, IObservable { protected: - IWebService::ICallback& callback_; - Orthanc::WebServiceParameters parameters_; - std::string uri_; - std::map headers_; - std::auto_ptr payload_; - bool success_; - std::string answer_; - NativeStoneApplicationContext& context_; - - public: - WebServiceCommandBase(MessageBroker& broker, - IWebService::ICallback& callback, - const Orthanc::WebServiceParameters& parameters, - const std::string& uri, - const std::map& headers, - Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context); - - virtual void Execute() = 0; - - virtual void Commit(); - }; - - class NewWebServiceCommandBase : public IOracleCommand, IObservable - { - protected: std::auto_ptr> successCallback_; std::auto_ptr> failureCallback_; Orthanc::WebServiceParameters parameters_; @@ -74,7 +48,7 @@ NativeStoneApplicationContext& context_; public: - NewWebServiceCommandBase(MessageBroker& broker, + WebServiceCommandBase(MessageBroker& broker, MessageHandler* successCallback, // takes ownership MessageHandler* failureCallback, // takes ownership const Orthanc::WebServiceParameters& parameters, diff -r 3897f9f28cfa -r b4abaeb783b1 Platforms/Generic/WebServiceGetCommand.cpp --- a/Platforms/Generic/WebServiceGetCommand.cpp Fri Sep 14 16:44:01 2018 +0200 +++ b/Platforms/Generic/WebServiceGetCommand.cpp Tue Sep 18 15:23:21 2018 +0200 @@ -13,7 +13,7 @@ * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . **/ @@ -25,14 +25,16 @@ namespace OrthancStone { + WebServiceGetCommand::WebServiceGetCommand(MessageBroker& broker, - IWebService::ICallback& callback, + MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback, // takes ownership const Orthanc::WebServiceParameters& parameters, const std::string& uri, const IWebService::Headers& headers, Orthanc::IDynamicObject* payload /* takes ownership */, NativeStoneApplicationContext& context) : - WebServiceCommandBase(broker, callback, parameters, uri, headers, payload, context) + WebServiceCommandBase(broker, successCallback, failureCallback, parameters, uri, headers, payload, context) { } @@ -51,31 +53,4 @@ success_ = client.Apply(answer_); } - NewWebServiceGetCommand::NewWebServiceGetCommand(MessageBroker& broker, - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& uri, - const IWebService::Headers& headers, - Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context) : - NewWebServiceCommandBase(broker, successCallback, failureCallback, parameters, uri, headers, payload, context) - { - } - - - void NewWebServiceGetCommand::Execute() - { - Orthanc::HttpClient client(parameters_, uri_); - client.SetTimeout(60); - client.SetMethod(Orthanc::HttpMethod_Get); - - for (IWebService::Headers::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) - { - client.AddHeader(it->first, it->second); - } - - success_ = client.Apply(answer_); - } - } diff -r 3897f9f28cfa -r b4abaeb783b1 Platforms/Generic/WebServiceGetCommand.h --- a/Platforms/Generic/WebServiceGetCommand.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Platforms/Generic/WebServiceGetCommand.h Tue Sep 18 15:23:21 2018 +0200 @@ -29,7 +29,8 @@ { public: WebServiceGetCommand(MessageBroker& broker, - IWebService::ICallback& callback, + MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback, // takes ownership const Orthanc::WebServiceParameters& parameters, const std::string& uri, const IWebService::Headers& headers, @@ -39,19 +40,4 @@ virtual void Execute(); }; - class NewWebServiceGetCommand : public NewWebServiceCommandBase - { - public: - NewWebServiceGetCommand(MessageBroker& broker, - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& uri, - const IWebService::Headers& headers, - Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context); - - virtual void Execute(); - }; - } diff -r 3897f9f28cfa -r b4abaeb783b1 Platforms/Generic/WebServicePostCommand.cpp --- a/Platforms/Generic/WebServicePostCommand.cpp Fri Sep 14 16:44:01 2018 +0200 +++ b/Platforms/Generic/WebServicePostCommand.cpp Tue Sep 18 15:23:21 2018 +0200 @@ -26,14 +26,15 @@ namespace OrthancStone { WebServicePostCommand::WebServicePostCommand(MessageBroker& broker, - IWebService::ICallback& callback, + MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback, // takes ownership const Orthanc::WebServiceParameters& parameters, const std::string& uri, const IWebService::Headers& headers, const std::string& body, Orthanc::IDynamicObject* payload /* takes ownership */, NativeStoneApplicationContext& context) : - WebServiceCommandBase(broker, callback, parameters, uri, headers, payload, context), + WebServiceCommandBase(broker, successCallback, failureCallback, parameters, uri, headers, payload, context), body_(body) { } diff -r 3897f9f28cfa -r b4abaeb783b1 Platforms/Generic/WebServicePostCommand.h --- a/Platforms/Generic/WebServicePostCommand.h Fri Sep 14 16:44:01 2018 +0200 +++ b/Platforms/Generic/WebServicePostCommand.h Tue Sep 18 15:23:21 2018 +0200 @@ -32,7 +32,8 @@ public: WebServicePostCommand(MessageBroker& broker, - IWebService::ICallback& callback, + MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback, // takes ownership const Orthanc::WebServiceParameters& parameters, const std::string& uri, const IWebService::Headers& headers, diff -r 3897f9f28cfa -r b4abaeb783b1 Resources/CMake/OrthancStoneConfiguration.cmake --- a/Resources/CMake/OrthancStoneConfiguration.cmake Fri Sep 14 16:44:01 2018 +0200 +++ b/Resources/CMake/OrthancStoneConfiguration.cmake Tue Sep 18 15:23:21 2018 +0200 @@ -285,6 +285,7 @@ ${ORTHANC_STONE_ROOT}/Framework/Messages/IObservable.h ${ORTHANC_STONE_ROOT}/Framework/Messages/IObserver.h ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageBroker.h + ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageForwarder.h ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageType.h ${ORTHANC_STONE_ROOT}/Framework/Messages/Promise.h diff -r 3897f9f28cfa -r b4abaeb783b1 UnitTestsSources/TestMessageBroker2.cpp --- a/UnitTestsSources/TestMessageBroker2.cpp Fri Sep 14 16:44:01 2018 +0200 +++ b/UnitTestsSources/TestMessageBroker2.cpp Tue Sep 18 15:23:21 2018 +0200 @@ -22,296 +22,291 @@ #include "gtest/gtest.h" #include "Framework/Messages/MessageBroker.h" +#include "Framework/Messages/Promise.h" +#include "Framework/Messages/IObservable.h" +#include "Framework/Messages/IObserver.h" +#include "Framework/Messages/MessageForwarder.h" -#include -#include -#include - -#include -#include -#include int testCounter = 0; namespace { - class IObserver; - class IObservable; - class Promise; +// class IObserver; +// class IObservable; +// class Promise; - enum MessageType - { - MessageType_Test1, - MessageType_Test2, +// enum MessageType +// { +// MessageType_Test1, +// MessageType_Test2, - MessageType_CustomMessage, - MessageType_LastGenericStoneMessage - }; +// MessageType_CustomMessage, +// MessageType_LastGenericStoneMessage +// }; - struct IMessage : public boost::noncopyable - { - MessageType messageType_; - public: - IMessage(const MessageType& messageType) - : messageType_(messageType) - {} - virtual ~IMessage() {} +// struct IMessage : public boost::noncopyable +// { +// MessageType messageType_; +// public: +// IMessage(const MessageType& messageType) +// : messageType_(messageType) +// {} +// virtual ~IMessage() {} - virtual int GetType() const {return messageType_;} - }; +// virtual int GetType() const {return messageType_;} +// }; - struct ICustomMessage : public IMessage - { - int customMessageType_; - public: - ICustomMessage(int customMessageType) - : IMessage(MessageType_CustomMessage), - customMessageType_(customMessageType) - {} - virtual ~ICustomMessage() {} +// struct ICustomMessage : public IMessage +// { +// int customMessageType_; +// public: +// ICustomMessage(int customMessageType) +// : IMessage(MessageType_CustomMessage), +// customMessageType_(customMessageType) +// {} +// virtual ~ICustomMessage() {} - virtual int GetType() const {return customMessageType_;} - }; +// virtual int GetType() const {return customMessageType_;} +// }; - // This is referencing an object and member function that can be notified - // by an IObservable. The object must derive from IO - // The member functions must be of type "void Function(const IMessage& message)" or reference a derived class of IMessage - class ICallable : public boost::noncopyable - { - public: - virtual ~ICallable() - { - } +// // This is referencing an object and member function that can be notified +// // by an IObservable. The object must derive from IO +// // The member functions must be of type "void Function(const IMessage& message)" or reference a derived class of IMessage +// class ICallable : public boost::noncopyable +// { +// public: +// virtual ~ICallable() +// { +// } - virtual void Apply(const IMessage& message) = 0; +// virtual void Apply(const IMessage& message) = 0; - virtual MessageType GetMessageType() const = 0; - virtual IObserver* GetObserver() const = 0; - }; +// virtual MessageType GetMessageType() const = 0; +// virtual IObserver* GetObserver() const = 0; +// }; - template - class Callable : public ICallable - { - private: - typedef void (TObserver::* MemberFunction) (const TMessage&); +// template +// class Callable : public ICallable +// { +// private: +// typedef void (TObserver::* MemberFunction) (const TMessage&); - TObserver& observer_; - MemberFunction function_; +// TObserver& observer_; +// MemberFunction function_; - public: - Callable(TObserver& observer, - MemberFunction function) : - observer_(observer), - function_(function) - { - } +// public: +// Callable(TObserver& observer, +// MemberFunction function) : +// observer_(observer), +// function_(function) +// { +// } - void ApplyInternal(const TMessage& message) - { - (observer_.*function_) (message); - } +// void ApplyInternal(const TMessage& message) +// { +// (observer_.*function_) (message); +// } - virtual void Apply(const IMessage& message) - { - ApplyInternal(dynamic_cast(message)); - } +// virtual void Apply(const IMessage& message) +// { +// ApplyInternal(dynamic_cast(message)); +// } - virtual MessageType GetMessageType() const - { - return static_cast(TMessage::Type); - } +// virtual MessageType GetMessageType() const +// { +// return static_cast(TMessage::Type); +// } - virtual IObserver* GetObserver() const - { - return &observer_; - } - }; +// virtual IObserver* GetObserver() const +// { +// return &observer_; +// } +// }; - /* - * This is a central message broker. It keeps track of all observers and knows - * when an observer is deleted. - * This way, it can prevent an observable to send a message to a delete observer. - */ - class MessageBroker : public boost::noncopyable - { +// /* +// * This is a central message broker. It keeps track of all observers and knows +// * when an observer is deleted. +// * This way, it can prevent an observable to send a message to a delete observer. +// */ +// class MessageBroker : public boost::noncopyable +// { - std::set activeObservers_; // the list of observers that are currently alive (that have not been deleted) +// std::set activeObservers_; // the list of observers that are currently alive (that have not been deleted) - public: +// public: - void Register(IObserver& observer) - { - activeObservers_.insert(&observer); - } +// void Register(IObserver& observer) +// { +// activeObservers_.insert(&observer); +// } - void Unregister(IObserver& observer) - { - activeObservers_.erase(&observer); - } +// void Unregister(IObserver& observer) +// { +// activeObservers_.erase(&observer); +// } - bool IsActive(IObserver* observer) - { - return activeObservers_.find(observer) != activeObservers_.end(); - } - }; +// bool IsActive(IObserver* observer) +// { +// return activeObservers_.find(observer) != activeObservers_.end(); +// } +// }; - class Promise : public boost::noncopyable - { - protected: - MessageBroker& broker_; +// class Promise : public boost::noncopyable +// { +// protected: +// MessageBroker& broker_; - ICallable* successCallable_; - ICallable* failureCallable_; +// ICallable* successCallable_; +// ICallable* failureCallable_; - public: - Promise(MessageBroker& broker) - : broker_(broker), - successCallable_(NULL), - failureCallable_(NULL) - { - } +// public: +// Promise(MessageBroker& broker) +// : broker_(broker), +// successCallable_(NULL), +// failureCallable_(NULL) +// { +// } - void Success(const IMessage& message) - { - // check the target is still alive in the broker - if (broker_.IsActive(successCallable_->GetObserver())) - { - successCallable_->Apply(message); - } - } +// void Success(const IMessage& message) +// { +// // check the target is still alive in the broker +// if (broker_.IsActive(successCallable_->GetObserver())) +// { +// successCallable_->Apply(message); +// } +// } - void Failure(const IMessage& message) - { - // check the target is still alive in the broker - if (broker_.IsActive(failureCallable_->GetObserver())) - { - failureCallable_->Apply(message); - } - } +// void Failure(const IMessage& message) +// { +// // check the target is still alive in the broker +// if (broker_.IsActive(failureCallable_->GetObserver())) +// { +// failureCallable_->Apply(message); +// } +// } - Promise& Then(ICallable* successCallable) - { - if (successCallable_ != NULL) - { - // TODO: throw throw new "Promise may only have a single success target" - } - successCallable_ = successCallable; - return *this; - } +// Promise& Then(ICallable* successCallable) +// { +// if (successCallable_ != NULL) +// { +// // TODO: throw throw new "Promise may only have a single success target" +// } +// successCallable_ = successCallable; +// return *this; +// } - Promise& Else(ICallable* failureCallable) - { - if (failureCallable_ != NULL) - { - // TODO: throw throw new "Promise may only have a single failure target" - } - failureCallable_ = failureCallable; - return *this; - } +// Promise& Else(ICallable* failureCallable) +// { +// if (failureCallable_ != NULL) +// { +// // TODO: throw throw new "Promise may only have a single failure target" +// } +// failureCallable_ = failureCallable; +// return *this; +// } - }; +// }; - class IObserver : public boost::noncopyable - { - protected: - MessageBroker& broker_; +// class IObserver : public boost::noncopyable +// { +// protected: +// MessageBroker& broker_; - public: - IObserver(MessageBroker& broker) - : broker_(broker) - { - broker_.Register(*this); - } +// public: +// IObserver(MessageBroker& broker) +// : broker_(broker) +// { +// broker_.Register(*this); +// } - virtual ~IObserver() - { - broker_.Unregister(*this); - } +// virtual ~IObserver() +// { +// broker_.Unregister(*this); +// } - }; +// }; - class IObservable : public boost::noncopyable - { - protected: - MessageBroker& broker_; +// class IObservable : public boost::noncopyable +// { +// protected: +// MessageBroker& broker_; - typedef std::map > Callables; - Callables callables_; - public: +// typedef std::map > Callables; +// Callables callables_; +// public: - IObservable(MessageBroker& broker) - : broker_(broker) - { - } +// IObservable(MessageBroker& broker) +// : broker_(broker) +// { +// } - virtual ~IObservable() - { - for (Callables::const_iterator it = callables_.begin(); - it != callables_.end(); ++it) - { - for (std::set::const_iterator - it2 = it->second.begin(); it2 != it->second.end(); ++it2) - { - delete *it2; - } - } - } +// virtual ~IObservable() +// { +// for (Callables::const_iterator it = callables_.begin(); +// it != callables_.end(); ++it) +// { +// for (std::set::const_iterator +// it2 = it->second.begin(); it2 != it->second.end(); ++it2) +// { +// delete *it2; +// } +// } +// } - void Register(ICallable* callable) - { - MessageType messageType = callable->GetMessageType(); +// void Register(ICallable* callable) +// { +// MessageType messageType = callable->GetMessageType(); - callables_[messageType].insert(callable); - } +// callables_[messageType].insert(callable); +// } - void EmitMessage(const IMessage& message) - { - Callables::const_iterator found = callables_.find(message.GetType()); +// void EmitMessage(const IMessage& message) +// { +// Callables::const_iterator found = callables_.find(message.GetType()); - if (found != callables_.end()) - { - for (std::set::const_iterator - it = found->second.begin(); it != found->second.end(); ++it) - { - if (broker_.IsActive((*it)->GetObserver())) - { - (*it)->Apply(message); - } - } - } - } +// if (found != callables_.end()) +// { +// for (std::set::const_iterator +// it = found->second.begin(); it != found->second.end(); ++it) +// { +// if (broker_.IsActive((*it)->GetObserver())) +// { +// (*it)->Apply(message); +// } +// } +// } +// } - }; +// }; - enum CustomMessageType - { - CustomMessageType_First = MessageType_LastGenericStoneMessage + 1, +// enum CustomMessageType +// { +// CustomMessageType_First = MessageType_LastGenericStoneMessage + 1, - CustomMessageType_Completed, - CustomMessageType_Increment - }; +// CustomMessageType_Completed, +// CustomMessageType_Increment +// }; + + using namespace OrthancStone; class MyObservable : public IObservable { public: - struct MyCustomMessage: public ICustomMessage + struct MyCustomMessage: public BaseMessage { int payload_; - enum - { - Type = CustomMessageType_Completed - }; MyCustomMessage(int payload) - : ICustomMessage(Type), + : BaseMessage(), payload_(payload) {} }; @@ -337,20 +332,30 @@ }; + class MyIntermediate : public IObserver, public IObservable + { + IObservable& observedObject_; + public: + MyIntermediate(MessageBroker& broker, IObservable& observedObject) + : IObserver(broker), + IObservable(broker), + observedObject_(observedObject) + { + observedObject_.RegisterObserverCallback(new MessageForwarder(broker, *this)); + } + }; + + class MyPromiseSource : public IObservable { Promise* currentPromise_; public: - struct MyPromiseMessage: public ICustomMessage + struct MyPromiseMessage: public BaseMessage { int increment; - enum - { - Type = CustomMessageType_Increment - }; MyPromiseMessage(int increment) - : ICustomMessage(Type), + : BaseMessage(), increment(increment) {} }; @@ -407,7 +412,7 @@ MyObserver observer(broker); // create a permanent connection between an observable and an observer - observable.Register(new Callable(observer, &MyObserver::HandleCompletedMessage)); + observable.RegisterObserverCallback(new Callable(observer, &MyObserver::HandleCompletedMessage)); testCounter = 0; observable.EmitMessage(MyObservable::MyCustomMessage(12)); @@ -426,7 +431,7 @@ MyObserver* observer = new MyObserver(broker); // create a permanent connection between an observable and an observer - observable.Register(new Callable(*observer, &MyObserver::HandleCompletedMessage)); + observable.RegisterObserverCallback(new Callable(*observer, &MyObserver::HandleCompletedMessage)); testCounter = 0; observable.EmitMessage(MyObservable::MyCustomMessage(12)); @@ -441,6 +446,27 @@ ASSERT_EQ(0, testCounter); } +TEST(MessageBroker2, TestMessageForwarderSimpleUseCase) +{ + MessageBroker broker; + MyObservable observable(broker); + MyIntermediate intermediate(broker, observable); + MyObserver observer(broker); + + // let the observer observers the intermediate that is actually forwarding the messages from the observable + intermediate.RegisterObserverCallback(new Callable(observer, &MyObserver::HandleCompletedMessage)); + + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(12)); + ASSERT_EQ(12, testCounter); + + // the connection is permanent; if we emit the same message again, the observer will be notified again + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(20)); + ASSERT_EQ(20, testCounter); +} + + TEST(MessageBroker2, TestPromiseSuccessFailure) { @@ -496,196 +522,3 @@ ASSERT_EQ(0, testCounter); } - - -//#include -//#include - -//#include -//#include -//#include -//#include - -//enum MessageType -//{ -// MessageType_SeriesDownloaded = 1 -//}; - - -//class IMessage : public boost::noncopyable -//{ -//private: -// MessageType type_; - -//public: -// IMessage(MessageType type) : -// type_(type) -// { -// } - -// virtual ~IMessage() -// { -// } - -// MessageType GetMessageType() const -// { -// return type_; -// } -//}; - - -//class IObserver : public boost::noncopyable -//{ -//public: -// virtual ~IObserver() -// { -// } -//}; - - -//class SeriesDownloadedMessage : public IMessage -//{ -//private: -// std::string value_; - -//public: -// enum -// { -// Type = MessageType_SeriesDownloaded -// }; - -// SeriesDownloadedMessage(const std::string& value) : -// IMessage(static_cast(Type)), -// value_(value) -// { -// } - -// const std::string& GetValue() const -// { -// return value_; -// } -//}; - - -//class MyObserver : public IObserver -//{ -//public: -// void OnSeriesDownloaded(const SeriesDownloadedMessage& message) -// { -// printf("received: [%s]\n", message.GetValue().c_str()); -// } -//}; - - - -//class ICallable : public boost::noncopyable // ne peut referencer que les classes de base -//{ -//public: -// virtual ~ICallable() -// { -// } - -// virtual void Apply(const IMessage& message) = 0; - -// virtual MessageType GetMessageType() const = 0; -//}; - - - -//template -//class Callable : public ICallable -//{ -//private: -// typedef void (Observer::* MemberFunction) (const Message&); - -// Observer& observer_; -// MemberFunction function_; - -//public: -// Callable(Observer& observer, -// MemberFunction function) : -// observer_(observer), -// function_(function) -// { -// } - -// void ApplyInternal(const Message& message) -// { -// (observer_.*function_) (message); -// } - -// virtual void Apply(const IMessage& message) -// { -// ApplyInternal(dynamic_cast(message)); -// } - -// virtual MessageType GetMessageType() const -// { -// return static_cast(Message::Type); -// } -//}; - - - -//class IObservable : public boost::noncopyable -//{ -//private: -// typedef std::map > Callables; - -// Callables callables_; - -//public: -// virtual ~IObservable() -// { -// for (Callables::const_iterator it = callables_.begin(); -// it != callables_.end(); ++it) -// { -// for (std::set::const_iterator -// it2 = it->second.begin(); it2 != it->second.end(); ++it2) -// { -// delete *it2; -// } -// } -// } - -// void Register(ICallable* callable) -// { -// MessageType type = callable->GetMessageType(); - -// callables_[type].insert(callable); -// } - -// void Emit(const IMessage& message) const -// { -// Callables::const_iterator found = callables_.find(message.GetMessageType()); - -// if (found != callables_.end()) -// { -// for (std::set::const_iterator -// it = found->second.begin(); it != found->second.end(); ++it) -// { -// (*it)->Apply(message); -// } -// } -// } -//}; - - - - -//int main() -//{ -// MyObserver observer; - -// SeriesDownloadedMessage message("coucou"); - -// IObservable observable; -// observable.Register(new Callable(observer, &MyObserver::OnSeriesDownloaded)); -// observable.Register(new Callable(observer, &MyObserver::OnSeriesDownloaded)); - -// SeriesDownloadedMessage message2("hello"); -// observable.Emit(message2); - -// printf("%d\n", SeriesDownloadedMessage::Type); -//}