Mercurial > hg > orthanc-stone
diff Samples/Sdl/Loader.cpp @ 633:b0652595b62a
Merge
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Thu, 09 May 2019 14:38:27 +0200 |
parents | ea8322566596 |
children | fb00a8be03e2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Sdl/Loader.cpp Thu May 09 14:38:27 2019 +0200 @@ -0,0 +1,1151 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 <http://www.gnu.org/licenses/>. + **/ + +// From Stone +#include "../../Framework/Messages/ICallable.h" +#include "../../Framework/Messages/IMessage.h" +#include "../../Framework/Messages/IObservable.h" +#include "../../Framework/Messages/MessageBroker.h" +#include "../../Framework/StoneInitialization.h" +#include "../../Framework/Toolbox/GeometryToolbox.h" +#include "../../Framework/Volumes/ImageBuffer3D.h" + +// From Orthanc framework +#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/PngWriter.h> +#include <Core/Logging.h> +#include <Core/MultiThreading/SharedMessageQueue.h> +#include <Core/OrthancException.h> +#include <Core/Toolbox.h> + +#include <json/reader.h> +#include <json/value.h> +#include <json/writer.h> + +#include <list> +#include <stdio.h> + + + +namespace Refactoring +{ + class IOracleCommand : public boost::noncopyable + { + public: + enum Type + { + Type_OrthancApi + }; + + virtual ~IOracleCommand() + { + } + + virtual Type GetType() const = 0; + }; + + + class IMessageEmitter : public boost::noncopyable + { + public: + virtual ~IMessageEmitter() + { + } + + virtual void EmitMessage(const OrthancStone::IObserver& observer, + const OrthancStone::IMessage& message) = 0; + }; + + + class IOracle : public boost::noncopyable + { + public: + virtual ~IOracle() + { + } + + virtual void Schedule(const OrthancStone::IObserver& receiver, + IOracleCommand* command) = 0; // Takes ownership + }; + + + + + class OracleCommandWithPayload : public IOracleCommand + { + private: + std::auto_ptr<Orthanc::IDynamicObject> payload_; + + public: + void SetPayload(Orthanc::IDynamicObject* payload) + { + if (payload == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + else + { + payload_.reset(payload); + } + } + + bool HasPayload() const + { + return (payload_.get() != NULL); + } + + const Orthanc::IDynamicObject& GetPayload() const + { + if (HasPayload()) + { + return *payload_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + }; + + + + typedef std::map<std::string, std::string> HttpHeaders; + + class OrthancApiOracleCommand : public OracleCommandWithPayload + { + public: + class SuccessMessage : public OrthancStone::OriginMessage<OrthancStone::MessageType_HttpRequestSuccess, // TODO + OrthancApiOracleCommand> + { + private: + HttpHeaders headers_; + std::string answer_; + + public: + SuccessMessage(const OrthancApiOracleCommand& command, + const HttpHeaders& answerHeaders, + std::string& answer /* will be swapped to avoid a memcpy() */) : + OriginMessage(command), + headers_(answerHeaders), + answer_(answer) + { + } + + const std::string& GetAnswer() const + { + return answer_; + } + + void ParseJsonBody(Json::Value& target) const + { + Json::Reader reader; + if (!reader.parse(answer_, target)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + const HttpHeaders& GetAnswerHeaders() const + { + return headers_; + } + }; + + + 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_; + std::string body_; + HttpHeaders headers_; + unsigned int timeout_; + + std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> > successCallback_; + std::auto_ptr< OrthancStone::MessageHandler<FailureMessage> > failureCallback_; + + public: + OrthancApiOracleCommand() : + method_(Orthanc::HttpMethod_Get), + uri_("/"), + timeout_(10) + { + } + + virtual Type GetType() const + { + return Type_OrthancApi; + } + + void SetMethod(Orthanc::HttpMethod method) + { + method_ = method; + } + + void SetUri(const std::string& uri) + { + uri_ = uri; + } + + void SetBody(const std::string& body) + { + body_ = body; + } + + void SetBody(const Json::Value& json) + { + Json::FastWriter writer; + body_ = writer.write(json); + } + + void SetHttpHeader(const std::string& key, + const std::string& value) + { + headers_[key] = value; + } + + Orthanc::HttpMethod GetMethod() const + { + return method_; + } + + const std::string& GetUri() const + { + return uri_; + } + + const std::string& GetBody() const + { + if (method_ == Orthanc::HttpMethod_Post || + method_ == Orthanc::HttpMethod_Put) + { + return body_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + const HttpHeaders& GetHttpHeaders() const + { + return headers_; + } + + void SetTimeout(unsigned int seconds) + { + timeout_ = seconds; + } + + unsigned int GetTimeout() const + { + return timeout_; + } + }; + + + + class NativeOracle : public IOracle + { + private: + class Item : public Orthanc::IDynamicObject + { + private: + const OrthancStone::IObserver& receiver_; + std::auto_ptr<IOracleCommand> command_; + + public: + Item(const OrthancStone::IObserver& receiver, + IOracleCommand* command) : + receiver_(receiver), + command_(command) + { + if (command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + const OrthancStone::IObserver& GetReceiver() const + { + return receiver_; + } + + const IOracleCommand& GetCommand() const + { + assert(command_.get() != NULL); + return *command_; + } + }; + + + enum State + { + State_Setup, + State_Running, + State_Stopped + }; + + + IMessageEmitter& emitter_; + Orthanc::WebServiceParameters orthanc_; + Orthanc::SharedMessageQueue queue_; + State state_; + boost::mutex mutex_; + std::vector<boost::thread*> workers_; + + + void Execute(const OrthancStone::IObserver& receiver, + const OrthancApiOracleCommand& command) + { + Orthanc::HttpClient client(orthanc_, command.GetUri()); + client.SetMethod(command.GetMethod()); + client.SetTimeout(command.GetTimeout()); + + 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; + + bool success; + try + { + success = client.Apply(answer, answerHeaders); + } + catch (Orthanc::OrthancException& e) + { + success = false; + } + + if (success) + { + OrthancApiOracleCommand::SuccessMessage message(command, answerHeaders, answer); + emitter_.EmitMessage(receiver, message); + } + else + { + OrthancApiOracleCommand::FailureMessage message(command, client.GetLastStatus()); + emitter_.EmitMessage(receiver, message); + } + } + + + + void Step() + { + std::auto_ptr<Orthanc::IDynamicObject> object(queue_.Dequeue(100)); + + if (object.get() != NULL) + { + const Item& item = dynamic_cast<Item&>(*object); + + try + { + switch (item.GetCommand().GetType()) + { + case IOracleCommand::Type_OrthancApi: + Execute(item.GetReceiver(), + dynamic_cast<const OrthancApiOracleCommand&>(item.GetCommand())); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception within the oracle: " << e.What(); + } + catch (...) + { + LOG(ERROR) << "Native exception within the oracle"; + } + } + } + + + static void Worker(NativeOracle* that) + { + assert(that != NULL); + + for (;;) + { + { + boost::mutex::scoped_lock lock(that->mutex_); + if (that->state_ != State_Running) + { + return; + } + } + + that->Step(); + } + } + + + void StopInternal() + { + { + boost::mutex::scoped_lock lock(mutex_); + + if (state_ == State_Setup || + state_ == State_Stopped) + { + return; + } + else + { + state_ = State_Stopped; + } + } + + for (size_t i = 0; i < workers_.size(); i++) + { + if (workers_[i] != NULL) + { + if (workers_[i]->joinable()) + { + workers_[i]->join(); + } + + delete workers_[i]; + } + } + } + + + public: + NativeOracle(IMessageEmitter& emitter) : + emitter_(emitter), + state_(State_Setup), + workers_(4) + { + } + + virtual ~NativeOracle() + { + StopInternal(); + } + + void SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc) + { + boost::mutex::scoped_lock lock(mutex_); + + if (state_ != State_Setup) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + orthanc_ = orthanc; + } + } + + void SetWorkersCount(unsigned int count) + { + boost::mutex::scoped_lock lock(mutex_); + + if (count <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else if (state_ != State_Setup) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + workers_.resize(count); + } + } + + void Start() + { + boost::mutex::scoped_lock lock(mutex_); + + if (state_ != State_Setup) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + state_ = State_Running; + + for (unsigned int i = 0; i < workers_.size(); i++) + { + workers_[i] = new boost::thread(Worker, this); + } + } + } + + void Stop() + { + StopInternal(); + } + + virtual void Schedule(const OrthancStone::IObserver& receiver, + IOracleCommand* command) + { + queue_.Enqueue(new Item(receiver, command)); + } + }; + + + + class NativeApplicationContext : public IMessageEmitter + { + private: + boost::shared_mutex mutex_; + OrthancStone::MessageBroker broker_; + OrthancStone::IObservable oracleObservable_; + + public: + NativeApplicationContext() : + oracleObservable_(broker_) + { + } + + + virtual void EmitMessage(const OrthancStone::IObserver& observer, + const OrthancStone::IMessage& message) + { + boost::unique_lock<boost::shared_mutex> lock(mutex_); + oracleObservable_.EmitMessage(observer, message); + } + + + class ReaderLock : public boost::noncopyable + { + private: + NativeApplicationContext& that_; + boost::shared_lock<boost::shared_mutex> lock_; + + public: + ReaderLock(NativeApplicationContext& that) : + that_(that), + lock_(that.mutex_) + { + } + }; + + + class WriterLock : public boost::noncopyable + { + private: + NativeApplicationContext& that_; + boost::unique_lock<boost::shared_mutex> lock_; + + public: + WriterLock(NativeApplicationContext& that) : + that_(that), + lock_(that.mutex_) + { + } + + OrthancStone::MessageBroker& GetBroker() + { + return that_.broker_; + } + + OrthancStone::IObservable& GetOracleObservable() + { + return that_.oracleObservable_; + } + }; + }; + + + + class DicomInstanceParameters : public boost::noncopyable + { + private: + Orthanc::DicomImageInformation imageInformation_; + OrthancStone::SopClassUid sopClassUid_; + double thickness_; + double pixelSpacingX_; + double pixelSpacingY_; + OrthancStone::CoordinateSystem3D geometry_; + OrthancStone::Vector frameOffsets_; + bool isColor_; + bool hasRescale_; + double rescaleOffset_; + double rescaleSlope_; + bool hasDefaultWindowing_; + float defaultWindowingCenter_; + float defaultWindowingWidth_; + Orthanc::PixelFormat expectedPixelFormat_; + + void ComputeDoseOffsets(const Orthanc::DicomMap& dicom) + { + // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html + + { + std::string increment; + + if (dicom.CopyToString(increment, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER, false)) + { + Orthanc::Toolbox::ToUpperCase(increment); + if (increment != "3004,000C") // This is the "Grid Frame Offset Vector" tag + { + LOG(ERROR) << "RT-DOSE: Bad value for the \"FrameIncrementPointer\" tag"; + return; + } + } + } + + if (!OrthancStone::LinearAlgebra::ParseVector(frameOffsets_, dicom, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR) || + frameOffsets_.size() < imageInformation_.GetNumberOfFrames()) + { + LOG(ERROR) << "RT-DOSE: No information about the 3D location of some slice(s)"; + frameOffsets_.clear(); + } + else + { + if (frameOffsets_.size() >= 2) + { + thickness_ = frameOffsets_[1] - frameOffsets_[0]; + + if (thickness_ < 0) + { + thickness_ = -thickness_; + } + } + } + } + + public: + DicomInstanceParameters(const Orthanc::DicomMap& dicom) : + imageInformation_(dicom) + { + if (imageInformation_.GetNumberOfFrames() <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + std::string s; + if (!dicom.CopyToString(s, Orthanc::DICOM_TAG_SOP_CLASS_UID, false)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + sopClassUid_ = OrthancStone::StringToSopClassUid(s); + } + + if (!dicom.ParseDouble(thickness_, Orthanc::DICOM_TAG_SLICE_THICKNESS)) + { + thickness_ = 100.0 * std::numeric_limits<double>::epsilon(); + } + + OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dicom); + + std::string position, orientation; + if (dicom.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) && + dicom.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false)) + { + geometry_ = OrthancStone::CoordinateSystem3D(position, orientation); + } + + if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) + { + ComputeDoseOffsets(dicom); + } + + isColor_ = (imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome1 && + imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome2); + + double doseGridScaling; + + if (dicom.ParseDouble(rescaleOffset_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) && + dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE)) + { + hasRescale_ = true; + } + else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING)) + { + hasRescale_ = true; + rescaleOffset_ = 0; + rescaleSlope_ = doseGridScaling; + } + else + { + hasRescale_ = false; + } + + OrthancStone::Vector c, w; + if (OrthancStone::LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) && + OrthancStone::LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) && + c.size() > 0 && + w.size() > 0) + { + hasDefaultWindowing_ = true; + defaultWindowingCenter_ = static_cast<float>(c[0]); + defaultWindowingWidth_ = static_cast<float>(w[0]); + } + else + { + hasDefaultWindowing_ = false; + } + + if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) + { + switch (imageInformation_.GetBitsStored()) + { + case 16: + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; + break; + + case 32: + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale32; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + else if (isColor_) + { + expectedPixelFormat_ = Orthanc::PixelFormat_RGB24; + } + else if (imageInformation_.IsSigned()) + { + expectedPixelFormat_ = Orthanc::PixelFormat_SignedGrayscale16; + } + else + { + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; + } + } + + const Orthanc::DicomImageInformation& GetImageInformation() const + { + return imageInformation_; + } + + OrthancStone::SopClassUid GetSopClassUid() const + { + return sopClassUid_; + } + + double GetThickness() const + { + return thickness_; + } + + double GetPixelSpacingX() const + { + return pixelSpacingX_; + } + + double GetPixelSpacingY() const + { + return pixelSpacingY_; + } + + const OrthancStone::CoordinateSystem3D& GetGeometry() const + { + return geometry_; + } + + OrthancStone::CoordinateSystem3D GetFrameGeometry(unsigned int frame) const + { + if (frame >= imageInformation_.GetNumberOfFrames()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) + { + if (frame >= frameOffsets_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + return OrthancStone::CoordinateSystem3D( + geometry_.GetOrigin() + frameOffsets_[frame] * geometry_.GetNormal(), + geometry_.GetAxisX(), + geometry_.GetAxisY()); + } + } + + bool FrameContainsPlane(unsigned int frame, + const OrthancStone::CoordinateSystem3D& plane) const + { + if (frame >= imageInformation_.GetNumberOfFrames()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + OrthancStone::CoordinateSystem3D tmp = geometry_; + + if (frame != 0) + { + tmp = GetFrameGeometry(frame); + } + + bool opposite; // Ignored + return (OrthancStone::GeometryToolbox::IsParallelOrOpposite( + opposite, tmp.GetNormal(), plane.GetNormal()) && + OrthancStone::LinearAlgebra::IsNear( + tmp.ProjectAlongNormal(tmp.GetOrigin()), + tmp.ProjectAlongNormal(plane.GetOrigin()), + thickness_ / 2.0)); + } + + bool IsColor() const + { + return isColor_; + } + + bool HasRescale() const + { + return hasRescale_; + } + + double GetRescaleOffset() const + { + if (hasRescale_) + { + return rescaleOffset_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + double GetRescaleSlope() const + { + if (hasRescale_) + { + return rescaleSlope_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + bool HasDefaultWindowing() const + { + return hasDefaultWindowing_; + } + + float GetDefaultWindowingCenter() const + { + if (hasDefaultWindowing_) + { + return defaultWindowingCenter_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + float GetDefaultWindowingWidth() const + { + if (hasDefaultWindowing_) + { + return defaultWindowingWidth_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + Orthanc::PixelFormat GetExpectedPixelFormat() const + { + return expectedPixelFormat_; + } + }; + + + class AxialVolumeOrthancLoader : public OrthancStone::IObserver + { + private: + class MessageHandler : public Orthanc::IDynamicObject + { + public: + virtual void Handle(const OrthancApiOracleCommand::SuccessMessage& message) const = 0; + }; + + void Handle(const OrthancApiOracleCommand::SuccessMessage& message) + { + dynamic_cast<const MessageHandler&>(message.GetOrigin().GetPayload()).Handle(message); + } + + + class LoadSeriesGeometryHandler : public MessageHandler + { + private: + AxialVolumeOrthancLoader& that_; + + public: + LoadSeriesGeometryHandler(AxialVolumeOrthancLoader& that) : + that_(that) + { + } + + virtual void Handle(const OrthancApiOracleCommand::SuccessMessage& message) const + { + Json::Value value; + message.ParseJsonBody(value); + + if (value.type() != Json::objectValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + Json::Value::Members instances = value.getMemberNames(); + + for (size_t i = 0; i < instances.size(); i++) + { + Orthanc::DicomMap dicom; + dicom.FromDicomAsJson(value[instances[i]]); + + DicomInstanceParameters instance(dicom); + } + } + }; + + + class LoadInstanceGeometryHandler : public MessageHandler + { + private: + AxialVolumeOrthancLoader& that_; + + public: + LoadInstanceGeometryHandler(AxialVolumeOrthancLoader& that) : + that_(that) + { + } + + virtual void Handle(const OrthancApiOracleCommand::SuccessMessage& message) const + { + Json::Value value; + message.ParseJsonBody(value); + + if (value.type() != Json::objectValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + Orthanc::DicomMap dicom; + dicom.FromDicomAsJson(value); + + DicomInstanceParameters instance(dicom); + } + }; + + + bool active_; + std::auto_ptr<OrthancStone::ImageBuffer3D> image_; + + + public: + AxialVolumeOrthancLoader(OrthancStone::IObservable& oracle) : + IObserver(oracle.GetBroker()), + active_(false) + { + oracle.RegisterObserverCallback( + new OrthancStone::Callable<AxialVolumeOrthancLoader, OrthancApiOracleCommand::SuccessMessage> + (*this, &AxialVolumeOrthancLoader::Handle)); + } + + void LoadSeries(IOracle& oracle, + const std::string& seriesId) + { + if (active_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + active_ = true; + + std::auto_ptr<Refactoring::OrthancApiOracleCommand> command(new Refactoring::OrthancApiOracleCommand); + command->SetUri("/series/" + seriesId + "/instances-tags"); + command->SetPayload(new LoadSeriesGeometryHandler(*this)); + + oracle.Schedule(*this, command.release()); + } + + void LoadInstance(IOracle& oracle, + const std::string& instanceId) + { + if (active_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + active_ = true; + + // Tag "3004-000c" is "Grid Frame Offset Vector", which is + // mandatory to read RT DOSE, but is too long to be returned by default + + // TODO => Should be part of a second call if needed + + std::auto_ptr<Refactoring::OrthancApiOracleCommand> command(new Refactoring::OrthancApiOracleCommand); + command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3004-000c"); + command->SetPayload(new LoadInstanceGeometryHandler(*this)); + + oracle.Schedule(*this, command.release()); + } + }; + +} + + + +class Toto : public OrthancStone::IObserver +{ +private: + void Handle(const Refactoring::OrthancApiOracleCommand::SuccessMessage& message) + { + Json::Value v; + message.ParseJsonBody(v); + + printf("ICI [%s]\n", v.toStyledString().c_str()); + } + + void Handle(const Refactoring::OrthancApiOracleCommand::FailureMessage& message) + { + printf("ERROR %d\n", message.GetHttpStatus()); + } + +public: + Toto(OrthancStone::IObservable& oracle) : + IObserver(oracle.GetBroker()) + { + oracle.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, Refactoring::OrthancApiOracleCommand::SuccessMessage>(*this, &Toto::Handle)); + } +}; + + +void Run(Refactoring::NativeApplicationContext& context) +{ + std::auto_ptr<Toto> toto; + std::auto_ptr<Refactoring::AxialVolumeOrthancLoader> loader1, loader2; + + { + Refactoring::NativeApplicationContext::WriterLock lock(context); + toto.reset(new Toto(lock.GetOracleObservable())); + loader1.reset(new Refactoring::AxialVolumeOrthancLoader(lock.GetOracleObservable())); + loader2.reset(new Refactoring::AxialVolumeOrthancLoader(lock.GetOracleObservable())); + } + + Refactoring::NativeOracle oracle(context); + + { + Orthanc::WebServiceParameters p; + //p.SetUrl("http://localhost:8043/"); + p.SetCredentials("orthanc", "orthanc"); + oracle.SetOrthancParameters(p); + } + + oracle.Start(); + + { + Json::Value v = Json::objectValue; + v["Level"] = "Series"; + v["Query"] = Json::objectValue; + + std::auto_ptr<Refactoring::OrthancApiOracleCommand> command(new Refactoring::OrthancApiOracleCommand); + command->SetMethod(Orthanc::HttpMethod_Post); + command->SetUri("/tools/find"); + command->SetBody(v); + + 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)); + + oracle.Stop(); +} + + + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + OrthancStone::StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); + + try + { + Refactoring::NativeApplicationContext context; + Run(context); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + OrthancStone::StoneFinalize(); + + return 0; +}