# HG changeset patch # User Sebastien Jodogne # Date 1432904445 -7200 # Node ID 772c8507c68dbcd91003787c1e198e91f528d028 # Parent 5c11c4e728ebb848538a24e6a57cb45f21cd7356# Parent d6971e18a324c716bb197126fce4a64321ffa082 integration mainline->query-retrieve diff -r d6971e18a324 -r 772c8507c68d CMakeLists.txt --- a/CMakeLists.txt Fri May 29 15:00:24 2015 +0200 +++ b/CMakeLists.txt Fri May 29 15:00:45 2015 +0200 @@ -70,6 +70,7 @@ set(ORTHANC_CORE_SOURCES Core/Cache/MemoryCache.cpp + Core/Cache/SharedArchive.cpp Core/ChunkedBuffer.cpp Core/Compression/BufferCompressor.cpp Core/Compression/ZlibCompressor.cpp @@ -172,6 +173,7 @@ OrthancServer/ExportedResource.cpp OrthancServer/ResourceFinder.cpp OrthancServer/DicomFindQuery.cpp + OrthancServer/QueryRetrieveHandler.cpp # From "lua-scripting" branch OrthancServer/DicomInstanceToStore.cpp diff -r d6971e18a324 -r 772c8507c68d Core/Cache/SharedArchive.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Cache/SharedArchive.cpp Fri May 29 15:00:45 2015 +0200 @@ -0,0 +1,134 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "SharedArchive.h" + + +#include "../Uuid.h" + + +namespace Orthanc +{ + void SharedArchive::RemoveInternal(const std::string& id) + { + Archive::iterator it = archive_.find(id); + + if (it != archive_.end()) + { + delete it->second; + archive_.erase(it); + } + } + + + SharedArchive::Accessor::Accessor(SharedArchive& that, + const std::string& id) : + lock_(that.mutex_) + { + Archive::iterator it = that.archive_.find(id); + + if (it == that.archive_.end()) + { + throw OrthancException(ErrorCode_InexistentItem); + } + else + { + that.lru_.MakeMostRecent(id); + item_ = it->second; + } + } + + + SharedArchive::SharedArchive(size_t maxSize) : + maxSize_(maxSize) + { + if (maxSize == 0) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + SharedArchive::~SharedArchive() + { + for (Archive::iterator it = archive_.begin(); + it != archive_.end(); it++) + { + delete it->second; + } + } + + + std::string SharedArchive::Add(IDynamicObject* obj) + { + boost::mutex::scoped_lock lock(mutex_); + + if (archive_.size() == maxSize_) + { + // The quota has been reached, remove the oldest element + std::string oldest = lru_.RemoveOldest(); + RemoveInternal(oldest); + } + + std::string id = Toolbox::GenerateUuid(); + RemoveInternal(id); // Should never be useful because of UUID + archive_[id] = obj; + lru_.Add(id); + + return id; + } + + + void SharedArchive::Remove(const std::string& id) + { + boost::mutex::scoped_lock lock(mutex_); + RemoveInternal(id); + lru_.Invalidate(id); + } + + + void SharedArchive::List(std::list& items) + { + items.clear(); + + boost::mutex::scoped_lock lock(mutex_); + + for (Archive::const_iterator it = archive_.begin(); + it != archive_.end(); it++) + { + items.push_back(it->first); + } + } +} + + diff -r d6971e18a324 -r 772c8507c68d Core/Cache/SharedArchive.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Cache/SharedArchive.h Fri May 29 15:00:45 2015 +0200 @@ -0,0 +1,85 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "LeastRecentlyUsedIndex.h" +#include "../IDynamicObject.h" + +#include +#include + +namespace Orthanc +{ + class SharedArchive : public boost::noncopyable + { + private: + typedef std::map Archive; + + size_t maxSize_; + boost::mutex mutex_; + Archive archive_; + Orthanc::LeastRecentlyUsedIndex lru_; + + void RemoveInternal(const std::string& id); + + public: + class Accessor : public boost::noncopyable + { + private: + boost::mutex::scoped_lock lock_; + IDynamicObject* item_; + + public: + Accessor(SharedArchive& that, + const std::string& id); + + IDynamicObject& GetItem() const + { + return *item_; + } + }; + + + SharedArchive(size_t maxSize); + + ~SharedArchive(); + + std::string Add(IDynamicObject* obj); // Takes the ownership + + void Remove(const std::string& id); + + void List(std::list& items); + }; +} + + diff -r d6971e18a324 -r 772c8507c68d Core/DicomFormat/DicomMap.cpp --- a/Core/DicomFormat/DicomMap.cpp Fri May 29 15:00:24 2015 +0200 +++ b/Core/DicomFormat/DicomMap.cpp Fri May 29 15:00:45 2015 +0200 @@ -273,6 +273,16 @@ result.SetValue(DICOM_TAG_ACCESSION_NUMBER, ""); result.SetValue(DICOM_TAG_PATIENT_ID, ""); result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, ""); + + // These tags are considered as "main" by Orthanc, but are not in the Series module + result.Remove(DicomTag(0x0008, 0x0070)); // Manufacturer + result.Remove(DicomTag(0x0008, 0x1010)); // Station name + result.Remove(DicomTag(0x0018, 0x0024)); // Sequence name + result.Remove(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES); + result.Remove(DICOM_TAG_IMAGES_IN_ACQUISITION); + result.Remove(DICOM_TAG_NUMBER_OF_SLICES); + result.Remove(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS); + result.Remove(DICOM_TAG_NUMBER_OF_TIME_SLICES); } void DicomMap::SetupFindInstanceTemplate(DicomMap& result) diff -r d6971e18a324 -r 772c8507c68d Core/DicomFormat/DicomMap.h diff -r d6971e18a324 -r 772c8507c68d Core/DicomFormat/DicomTag.cpp --- a/Core/DicomFormat/DicomTag.cpp Fri May 29 15:00:24 2015 +0200 +++ b/Core/DicomFormat/DicomTag.cpp Fri May 29 15:00:45 2015 +0200 @@ -118,11 +118,10 @@ } - void DicomTag::GetTagsForModule(std::set& target, + void DicomTag::AddTagsForModule(std::set& target, DicomModule module) { // REFERENCE: 11_03pu.pdf, DICOM PS 3.3 2011 - Information Object Definitions - target.clear(); switch (module) { diff -r d6971e18a324 -r 772c8507c68d Core/DicomFormat/DicomTag.h --- a/Core/DicomFormat/DicomTag.h Fri May 29 15:00:24 2015 +0200 +++ b/Core/DicomFormat/DicomTag.h Fri May 29 15:00:45 2015 +0200 @@ -84,7 +84,7 @@ friend std::ostream& operator<< (std::ostream& o, const DicomTag& tag); - static void GetTagsForModule(std::set& target, + static void AddTagsForModule(std::set& target, DicomModule module); bool IsIdentifier() const; diff -r d6971e18a324 -r 772c8507c68d Core/RestApi/RestApi.cpp --- a/Core/RestApi/RestApi.cpp Fri May 29 15:00:24 2015 +0200 +++ b/Core/RestApi/RestApi.cpp Fri May 29 15:00:45 2015 +0200 @@ -163,7 +163,7 @@ const GetArguments& getArguments, const std::string& postData) { - RestApiOutput wrappedOutput(output); + RestApiOutput wrappedOutput(output, method); #if ORTHANC_PUGIXML_ENABLED == 1 // Look if the user wishes XML answers instead of JSON diff -r d6971e18a324 -r 772c8507c68d Core/RestApi/RestApiCall.cpp --- a/Core/RestApi/RestApiCall.cpp Fri May 29 15:00:24 2015 +0200 +++ b/Core/RestApi/RestApiCall.cpp Fri May 29 15:00:45 2015 +0200 @@ -41,4 +41,17 @@ Json::Reader reader; return reader.parse(request, result); } + + + std::string RestApiCall::FlattenUri() const + { + std::string s = "/"; + + for (size_t i = 0; i < fullUri_.size(); i++) + { + s += fullUri_[i] + "/"; + } + + return s; + } } diff -r d6971e18a324 -r 772c8507c68d Core/RestApi/RestApiCall.h --- a/Core/RestApi/RestApiCall.h Fri May 29 15:00:24 2015 +0200 +++ b/Core/RestApi/RestApiCall.h Fri May 29 15:00:45 2015 +0200 @@ -114,6 +114,8 @@ HttpHandler::ParseCookies(result, httpHeaders_); } + std::string FlattenUri() const; + virtual bool ParseJsonRequest(Json::Value& result) const = 0; }; } diff -r d6971e18a324 -r 772c8507c68d Core/RestApi/RestApiOutput.cpp --- a/Core/RestApi/RestApiOutput.cpp Fri May 29 15:00:24 2015 +0200 +++ b/Core/RestApi/RestApiOutput.cpp Fri May 29 15:00:45 2015 +0200 @@ -40,8 +40,10 @@ namespace Orthanc { - RestApiOutput::RestApiOutput(HttpOutput& output) : + RestApiOutput::RestApiOutput(HttpOutput& output, + HttpMethod method) : output_(output), + method_(method), convertJsonToXml_(false) { alreadySent_ = false; @@ -55,7 +57,14 @@ { if (!alreadySent_) { - output_.SendStatus(HttpStatus_404_NotFound); + if (method_ == HttpMethod_Post) + { + output_.SendStatus(HttpStatus_400_BadRequest); + } + else + { + output_.SendStatus(HttpStatus_404_NotFound); + } } } diff -r d6971e18a324 -r 772c8507c68d Core/RestApi/RestApiOutput.h --- a/Core/RestApi/RestApiOutput.h Fri May 29 15:00:24 2015 +0200 +++ b/Core/RestApi/RestApiOutput.h Fri May 29 15:00:45 2015 +0200 @@ -43,13 +43,15 @@ { private: HttpOutput& output_; - bool alreadySent_; - bool convertJsonToXml_; + HttpMethod method_; + bool alreadySent_; + bool convertJsonToXml_; void CheckStatus(); public: - RestApiOutput(HttpOutput& output); + RestApiOutput(HttpOutput& output, + HttpMethod method); ~RestApiOutput(); diff -r d6971e18a324 -r 772c8507c68d NEWS --- a/NEWS Fri May 29 15:00:24 2015 +0200 +++ b/NEWS Fri May 29 15:00:45 2015 +0200 @@ -4,6 +4,8 @@ Major ----- +* DICOM Query/Retrieve available from Orthanc Explorer +* C-Move SCU and C-Find SCU are accessible through the REST API * "?expand" flag for URIs "/patients", "/studies" and "/series" * "/tools/find" URI to search for DICOM resources from REST * Support of FreeBSD diff -r d6971e18a324 -r 772c8507c68d OrthancExplorer/explorer.html --- a/OrthancExplorer/explorer.html Fri May 29 15:00:24 2015 +0200 +++ b/OrthancExplorer/explorer.html Fri May 29 15:00:45 2015 +0200 @@ -30,6 +30,7 @@ + @@ -37,7 +38,10 @@

Find a patient

Plugins - Upload DICOM +