# HG changeset patch # User Benjamin Golinvaux # Date 1559045926 -7200 # Node ID f38c1fc08655e5ebd5229bbc3140ad9876a6fa13 # Parent 7efcbae4717ec24bc1c87445d2be47726a532d81# Parent d3197e0e321dd98fca74c69dd6fa9d7a9aefed31 Merge diff -r 7efcbae4717e -r f38c1fc08655 Framework/Oracle/OracleCommandWithPayload.cpp --- a/Framework/Oracle/OracleCommandWithPayload.cpp Tue May 28 14:15:03 2019 +0200 +++ b/Framework/Oracle/OracleCommandWithPayload.cpp Tue May 28 14:18:46 2019 +0200 @@ -38,7 +38,7 @@ } - const Orthanc::IDynamicObject& OracleCommandWithPayload::GetPayload() const + Orthanc::IDynamicObject& OracleCommandWithPayload::GetPayload() const { if (HasPayload()) { diff -r 7efcbae4717e -r f38c1fc08655 Framework/Oracle/OracleCommandWithPayload.h --- a/Framework/Oracle/OracleCommandWithPayload.h Tue May 28 14:15:03 2019 +0200 +++ b/Framework/Oracle/OracleCommandWithPayload.h Tue May 28 14:18:46 2019 +0200 @@ -42,7 +42,7 @@ return (payload_.get() != NULL); } - const Orthanc::IDynamicObject& GetPayload() const; + Orthanc::IDynamicObject& GetPayload() const; Orthanc::IDynamicObject* ReleasePayload(); }; diff -r 7efcbae4717e -r f38c1fc08655 Samples/Sdl/Loader.cpp --- a/Samples/Sdl/Loader.cpp Tue May 28 14:15:03 2019 +0200 +++ b/Samples/Sdl/Loader.cpp Tue May 28 14:18:46 2019 +0200 @@ -532,6 +532,7 @@ + // TODO - Refactor using LoaderStateMachine? class OrthancSeriesVolumeProgressiveLoader : public IObserver, public IObservable, @@ -1021,10 +1022,10 @@ /** - This class is supplied with Oracle commands and will schedule up to - simultaneousDownloads_ of them at the same time, then will schedule the - rest once slots become available. It is used, a.o., by the - OrtancMultiframeVolumeLoader class. + This class is supplied with Oracle commands and will schedule up to + simultaneousDownloads_ of them at the same time, then will schedule the + rest once slots become available. It is used, a.o., by the + OrtancMultiframeVolumeLoader class. */ class LoaderStateMachine : public IObserver { @@ -1056,17 +1057,17 @@ return dynamic_cast(that_); } - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } - virtual void Handle(const GetOrthancImageCommand::SuccessMessage& message) const + virtual void Handle(const GetOrthancImageCommand::SuccessMessage& message) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } - virtual void Handle(const GetOrthancWebViewerJpegCommand::SuccessMessage& message) const + virtual void Handle(const GetOrthancWebViewerJpegCommand::SuccessMessage& message) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } @@ -1126,20 +1127,32 @@ { delete *it; } + + pendingCommands_.clear(); } - void HandleException(const OracleCommandExceptionMessage& message) + void HandleExceptionMessage(const OracleCommandExceptionMessage& message) { LOG(ERROR) << "Error in the state machine, stopping all processing"; Clear(); } template - void Handle(const T& message) + void HandleSuccessMessage(const T& message) { - dynamic_cast(message.GetOrigin().GetPayload()).Handle(message); + assert(activeCommands_ > 0); activeCommands_--; - Step(); + + try + { + dynamic_cast(message.GetOrigin().GetPayload()).Handle(message); + Step(); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Error in the state machine, stopping all processing: " << e.What(); + Clear(); + } } typedef std::list PendingCommands; @@ -1161,19 +1174,19 @@ { oracleObservable.RegisterObserverCallback( new Callable - (*this, &LoaderStateMachine::Handle)); + (*this, &LoaderStateMachine::HandleSuccessMessage)); oracleObservable.RegisterObserverCallback( new Callable - (*this, &LoaderStateMachine::Handle)); + (*this, &LoaderStateMachine::HandleSuccessMessage)); oracleObservable.RegisterObserverCallback( new Callable - (*this, &LoaderStateMachine::Handle)); + (*this, &LoaderStateMachine::HandleSuccessMessage)); oracleObservable.RegisterObserverCallback( new Callable - (*this, &LoaderStateMachine::HandleException)); + (*this, &LoaderStateMachine::HandleExceptionMessage)); } virtual ~LoaderStateMachine() @@ -1228,7 +1241,7 @@ } - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { // Complete the DICOM tags with just-received "Grid Frame Offset Vector" std::string s = Orthanc::Toolbox::StripSpaces(message.GetAnswer()); @@ -1262,7 +1275,7 @@ { } - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { OrthancMultiframeVolumeLoader& loader = GetLoader(); @@ -1306,7 +1319,7 @@ { } - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { GetLoader().SetTransferSyntax(message.GetAnswer()); } @@ -1321,7 +1334,7 @@ { } - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { GetLoader().SetUncompressedPixelData(message.GetAnswer()); } @@ -1355,8 +1368,8 @@ return; } /* - 1.2.840.10008.1.2 Implicit VR Endian: Default Transfer Syntax for DICOM - 1.2.840.10008.1.2.1 Explicit VR Little Endian + 1.2.840.10008.1.2 Implicit VR Endian: Default Transfer Syntax for DICOM + 1.2.840.10008.1.2.1 Explicit VR Little Endian 1.2.840.10008.1.2.2 Explicit VR Big Endian See https://www.dicomlibrary.com/dicom/transfer-syntax/ @@ -1548,17 +1561,17 @@ - class VolumeImageReslicer : public IVolumeSlicer + class DicomVolumeImageReslicer : public IVolumeSlicer { private: class Slice : public IExtractedSlice { private: - VolumeImageReslicer& that_; - CoordinateSystem3D cuttingPlane_; + DicomVolumeImageReslicer& that_; + CoordinateSystem3D cuttingPlane_; public: - Slice(VolumeImageReslicer& that, + Slice(DicomVolumeImageReslicer& that, const CoordinateSystem3D& cuttingPlane) : that_(that), cuttingPlane_(cuttingPlane) @@ -1575,7 +1588,7 @@ return that_.volume_->GetRevision(); } - virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, // possibly absent + virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, const CoordinateSystem3D& cuttingPlane) { VolumeReslicer& reslicer = that_.reslicer_; @@ -1621,7 +1634,7 @@ VolumeReslicer reslicer_; public: - VolumeImageReslicer(const boost::shared_ptr& volume) : + DicomVolumeImageReslicer(const boost::shared_ptr& volume) : volume_(volume) { if (volume.get() == NULL) @@ -1666,34 +1679,134 @@ class DicomStructureSetLoader : - public IObserver, + public LoaderStateMachine, public IVolumeSlicer { private: + class AddReferencedInstance : public State + { + private: + std::string instanceId_; + + public: + AddReferencedInstance(DicomStructureSetLoader& that, + const std::string& instanceId) : + State(that), + instanceId_(instanceId) + { + } + + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) + { + Json::Value tags; + message.ParseJsonBody(tags); + + Orthanc::DicomMap dicom; + dicom.FromDicomAsJson(tags); + + DicomStructureSetLoader& loader = GetLoader(); + loader.content_->AddReferencedSlice(dicom); + + loader.countProcessedInstances_ ++; + assert(loader.countProcessedInstances_ <= loader.countReferencedInstances_); + + if (loader.countProcessedInstances_ == loader.countReferencedInstances_) + { + // All the referenced instances have been loaded, finalize the RT-STRUCT + loader.content_->CheckReferencedSlices(); + loader.revision_++; + } + } + }; + + // State that converts a "SOP Instance UID" to an Orthanc identifier + class LookupInstance : public State + { + private: + std::string sopInstanceUid_; + + public: + LookupInstance(DicomStructureSetLoader& that, + const std::string& sopInstanceUid) : + State(that), + sopInstanceUid_(sopInstanceUid) + { + } + + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) + { + DicomStructureSetLoader& loader = GetLoader(); + + Json::Value lookup; + message.ParseJsonBody(lookup); + + 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_UnknownResource); + } + + const std::string instanceId = lookup[0]["ID"].asString(); + + { + std::auto_ptr command(new OrthancRestApiCommand); + command->SetHttpHeader("Accept-Encoding", "gzip"); + command->SetUri("/instances/" + instanceId + "/tags"); + command->SetPayload(new AddReferencedInstance(loader, instanceId)); + Schedule(command.release()); + } + } + }; + + class LoadStructure : public State + { + public: + LoadStructure(DicomStructureSetLoader& that) : + State(that) + { + } + + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) + { + DicomStructureSetLoader& loader = GetLoader(); + + { + OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer()); + loader.content_.reset(new DicomStructureSet(dicom)); + } + + std::set instances; + loader.content_->GetReferencedInstances(instances); + + loader.countReferencedInstances_ = instances.size(); + + for (std::set::const_iterator + it = instances.begin(); it != instances.end(); ++it) + { + std::auto_ptr command(new OrthancRestApiCommand); + command->SetUri("/tools/lookup"); + command->SetMethod(Orthanc::HttpMethod_Post); + command->SetBody(*it); + command->SetPayload(new LookupInstance(loader, *it)); + Schedule(command.release()); + + printf("[%s]\n", it->c_str()); + } + } + }; + + + std::auto_ptr content_; - IOracle& oracle_; - bool active_; uint64_t revision_; std::string instanceId_; - - void Handle(const OrthancRestApiCommand::SuccessMessage& message) - { - assert(active_); - - { - OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer()); - content_.reset(new DicomStructureSet(dicom)); - } - - std::set instances; - content_->GetReferencedInstances(instances); - - for (std::set::const_iterator - it = instances.begin(); it != instances.end(); ++it) - { - printf("[%s]\n", it->c_str()); - } - } + unsigned int countProcessedInstances_; + unsigned int countReferencedInstances_; class Slice : public IExtractedSlice @@ -1735,6 +1848,7 @@ assert(isValid_); std::auto_ptr layer(new PolylineSceneLayer); + layer->SetThickness(2); for (size_t i = 0; i < content_.GetStructuresCount(); i++) { @@ -1749,7 +1863,7 @@ PolylineSceneLayer::Chain chain; chain.resize(polygons[j].size()); - for (size_t k = 0; k < polygons[i].size(); k++) + for (size_t k = 0; k < polygons[j].size(); k++) { chain[k] = ScenePoint2D(polygons[j][k].first, polygons[j][k].second); } @@ -1759,8 +1873,6 @@ } } - printf("OK\n"); - return layer.release(); } }; @@ -1768,34 +1880,26 @@ public: DicomStructureSetLoader(IOracle& oracle, IObservable& oracleObservable) : - IObserver(oracleObservable.GetBroker()), - oracle_(oracle), - active_(false), - revision_(0) + LoaderStateMachine(oracle, oracleObservable), + revision_(0), + countProcessedInstances_(0), + countReferencedInstances_(0) { - oracleObservable.RegisterObserverCallback( - new Callable - (*this, &DicomStructureSetLoader::Handle)); } void LoadInstance(const std::string& instanceId) { - if (active_) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else + Start(); + + instanceId_ = instanceId; + { - active_ = true; - instanceId_ = instanceId; - - { - std::auto_ptr command(new OrthancRestApiCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3006-0050"); - oracle_.Schedule(*this, command.release()); - } + std::auto_ptr command(new OrthancRestApiCommand); + command->SetHttpHeader("Accept-Encoding", "gzip"); + command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3006-0050"); + command->SetPayload(new LoadStructure(*this)); + Schedule(command.release()); } } @@ -2238,7 +2342,7 @@ toto->SetVolume1(0, ctLoader, new OrthancStone::GrayscaleStyleConfigurator); #else { - boost::shared_ptr reslicer(new OrthancStone::VolumeImageReslicer(ct)); + boost::shared_ptr reslicer(new OrthancStone::DicomVolumeImageReslicer(ct)); toto->SetVolume1(0, reslicer, new OrthancStone::GrayscaleStyleConfigurator); } #endif @@ -2340,8 +2444,13 @@ //rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT #else //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT - doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE + //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT + + // 2017-05-16 + ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT + doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad"); // RT-DOSE + rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT #endif // 2015-01-28-Multiframe //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279"); // Multiframe CT