Mercurial > hg > orthanc-stone
view Framework/Loaders/DicomStructureSetLoader.cpp @ 971:bc7b249dfbd0 toa2019082902
Added EMSCRIPTEN_FETCH_REPLACE flag to requests to prevent stale cache results
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Thu, 29 Aug 2019 13:11:49 +0200 |
parents | 48710d53086f |
children | 262a0244e9b2 |
line wrap: on
line source
/** * 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/>. **/ #include "DicomStructureSetLoader.h" #include "../Scene2D/PolylineSceneLayer.h" #include "../Toolbox/GeometryToolbox.h" #if 0 bool logbgo233 = false; bool logbgo115 = false; #endif namespace OrthancStone { #if 0 void DumpDicomMap(std::ostream& o, const Orthanc::DicomMap& dicomMap) { using namespace std; //ios_base::fmtflags state = o.flags(); //o.flags(ios::right | ios::hex); //o << "(" << setfill('0') << setw(4) << tag.GetGroup() // << "," << setw(4) << tag.GetElement() << ")"; //o.flags(state); Json::Value val; dicomMap.Serialize(val); o << val; //return o; } #endif class DicomStructureSetLoader::AddReferencedInstance : public LoaderStateMachine::State { private: std::string instanceId_; public: AddReferencedInstance(DicomStructureSetLoader& that, const std::string& instanceId) : State(that), instanceId_(instanceId) { } virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { #if 0 if (logbgo115) LOG(TRACE) << "DicomStructureSetLoader::AddReferencedInstance::Handle() (SUCCESS)"; #endif Json::Value tags; message.ParseJsonBody(tags); Orthanc::DicomMap dicom; dicom.FromDicomAsJson(tags); DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); #if 0 { std::stringstream ss; //DumpDicomMap(ss, dicom); std::string dicomMapStr = ss.str(); if (logbgo115) LOG(TRACE) << " DicomStructureSetLoader::AddReferencedInstance::Handle() about to call AddReferencedSlice on dicom = " << dicomMapStr; } #endif 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_++; loader.SetStructuresReady(); } } }; // State that converts a "SOP Instance UID" to an Orthanc identifier class DicomStructureSetLoader::LookupInstance : public LoaderStateMachine::State { private: std::string sopInstanceUid_; public: LookupInstance(DicomStructureSetLoader& that, const std::string& sopInstanceUid) : State(that), sopInstanceUid_(sopInstanceUid) { } virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { #if 0 LOG(TRACE) << "DicomStructureSetLoader::LookupInstance::Handle() (SUCCESS)"; #endif DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); 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") { std::stringstream msg; msg << "Unknown resource! message.GetAnswer() = " << message.GetAnswer() << " message.GetAnswerHeaders() = "; for (OrthancRestApiCommand::HttpHeaders::const_iterator it = message.GetAnswerHeaders().begin(); it != message.GetAnswerHeaders().end(); ++it) { msg << "\nkey: \"" << it->first << "\" value: \"" << it->second << "\"\n"; } const std::string msgStr = msg.str(); LOG(ERROR) << msgStr; throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } const std::string instanceId = lookup[0]["ID"].asString(); { #if 0 if(logbgo115) LOG(TRACE) << "DicomStructureSetLoader::LookupInstance::Handle() (SUCCESS)"; #endif std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); command->SetHttpHeader("Accept-Encoding", "gzip"); std::string uri = "/instances/" + instanceId + "/tags"; command->SetUri(uri); command->SetPayload(new AddReferencedInstance(loader, instanceId)); #if 0 if (logbgo115) LOG(TRACE) << " DicomStructureSetLoader::LookupInstance::Handle() about to schedule request with AddReferencedInstance subsequent command on uri \"" << uri << "\""; #endif Schedule(command.release()); #if 0 if (logbgo115) LOG(TRACE) << " DicomStructureSetLoader::LookupInstance::Handle() request+command scheduled"; #endif } } }; class DicomStructureSetLoader::LoadStructure : public LoaderStateMachine::State { public: LoadStructure(DicomStructureSetLoader& that) : State(that) { } virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) { #if 0 if (logbgo115) LOG(TRACE) << "DicomStructureSetLoader::LoadStructure::Handle() (SUCCESS)"; #endif DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); { OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer()); loader.content_.reset(new DicomStructureSet(dicom)); } std::set<std::string> instances; loader.content_->GetReferencedInstances(instances); loader.countReferencedInstances_ = static_cast<unsigned int>(instances.size()); for (std::set<std::string>::const_iterator it = instances.begin(); it != instances.end(); ++it) { std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); #if 0 if (logbgo115) LOG(TRACE) << " DicomStructureSetLoader::LoadStructure::Handle() about to schedule /tools/lookup command with LookupInstance on result"; #endif command->SetUri("/tools/lookup"); command->SetMethod(Orthanc::HttpMethod_Post); command->SetBody(*it); // The following headers have been commented out because // they were causing issues in the reverse proxy in a dev scenario. // They should NOT be required for POST requests //command->SetHttpHeader("pragma", "no-cache"); //command->SetHttpHeader("cache-control", "no-cache"); #if 0 std::string itStr(*it); if(itStr == "1.3.12.2.1107.5.1.4.66930.30000018062412550879500002198") { if (logbgo115) LOG(ERROR) << "******** BOGUS LOOKUPS FROM NOW ON ***********"; logbgo233 = true; } #endif command->SetPayload(new LookupInstance(loader, *it)); //LOG(TRACE) << "About to schedule a /tools/lookup POST request. URI = " << command->GetUri() << " Body size = " << (*it).size() << " Body = " << (*it) << "\n"; Schedule(command.release()); #if 0 if (logbgo115) LOG(TRACE) << " DicomStructureSetLoader::LoadStructure::Handle() request scheduled. Command will be LookupInstance and post body is " << *it; #endif } } }; class DicomStructureSetLoader::Slice : public IExtractedSlice { private: const DicomStructureSet& content_; uint64_t revision_; bool isValid_; public: Slice(const DicomStructureSet& content, uint64_t revision, const CoordinateSystem3D& cuttingPlane) : content_(content), revision_(revision) { bool opposite; const Vector normal = content.GetNormal(); isValid_ = ( GeometryToolbox::IsParallelOrOpposite(opposite, normal, cuttingPlane.GetNormal()) || GeometryToolbox::IsParallelOrOpposite(opposite, normal, cuttingPlane.GetAxisX()) || GeometryToolbox::IsParallelOrOpposite(opposite, normal, cuttingPlane.GetAxisY())); } virtual bool IsValid() { return isValid_; } virtual uint64_t GetRevision() { return revision_; } virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, const CoordinateSystem3D& cuttingPlane) { assert(isValid_); std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); layer->SetThickness(2); for (size_t i = 0; i < content_.GetStructuresCount(); i++) { const Color& color = content_.GetStructureColor(i); std::vector< std::vector<DicomStructureSet::PolygonPoint> > polygons; if (content_.ProjectStructure(polygons, i, cuttingPlane)) { for (size_t j = 0; j < polygons.size(); j++) { PolylineSceneLayer::Chain chain; chain.resize(polygons[j].size()); for (size_t k = 0; k < polygons[j].size(); k++) { chain[k] = ScenePoint2D(polygons[j][k].first, polygons[j][k].second); } layer->AddChain(chain, true /* closed */, color); } } } return layer.release(); } }; DicomStructureSetLoader::DicomStructureSetLoader(IOracle& oracle, IObservable& oracleObservable) : LoaderStateMachine(oracle, oracleObservable), IObservable(oracleObservable.GetBroker()), revision_(0), countProcessedInstances_(0), countReferencedInstances_(0), structuresReady_(false) { } DicomStructureSetLoader::~DicomStructureSetLoader() { LOG(TRACE) << "DicomStructureSetLoader::~DicomStructureSetLoader()"; } void DicomStructureSetLoader::LoadInstance(const std::string& instanceId) { Start(); instanceId_ = instanceId; { std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); command->SetHttpHeader("Accept-Encoding", "gzip"); std::string uri = "/instances/" + instanceId + "/tags?ignore-length=3006-0050"; #if 0 if (logbgo115) LOG(TRACE) << "DicomStructureSetLoader::LoadInstance() instanceId = " << instanceId << " | uri = \"" << uri << "\""; #endif command->SetUri(uri); command->SetPayload(new LoadStructure(*this)); Schedule(command.release()); #if 0 if (logbgo115) LOG(TRACE) << "DicomStructureSetLoader::LoadInstance() command (with LoadStructure) scheduled."; #endif } } IVolumeSlicer::IExtractedSlice* DicomStructureSetLoader::ExtractSlice(const CoordinateSystem3D& cuttingPlane) { if (content_.get() == NULL) { // Geometry is not available yet return new IVolumeSlicer::InvalidSlice; } else { return new Slice(*content_, revision_, cuttingPlane); } } void DicomStructureSetLoader::SetStructuresReady() { ORTHANC_ASSERT(!structuresReady_); structuresReady_ = true; BroadcastMessage(DicomStructureSetLoader::StructuresReady(*this)); } bool DicomStructureSetLoader::AreStructuresReady() const { return structuresReady_; } }