Orthanc/OrthancServer/Sources/ServerEnumerations.cpp
2025-06-23 19:07:37 +05:30

668 lines
19 KiB
C++

/**
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
* Copyright (C) 2017-2023 Osimis S.A., Belgium
* Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
* Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, 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.
*
* 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 <http://www.gnu.org/licenses/>.
**/
#include "PrecompiledHeadersServer.h"
#include "ServerEnumerations.h"
#include "../../OrthancFramework/Sources/OrthancException.h"
#include "../../OrthancFramework/Sources/EnumerationDictionary.h"
#include "../../OrthancFramework/Sources/Logging.h"
#include "../../OrthancFramework/Sources/Toolbox.h"
#include <boost/thread.hpp>
namespace Orthanc
{
typedef std::map<FileContentType, std::string> MimeTypes;
static boost::mutex enumerationsMutex_;
static EnumerationDictionary<MetadataType> dictMetadataType_;
static EnumerationDictionary<FileContentType> dictContentType_;
static MimeTypes mimeTypes_;
void InitializeServerEnumerations()
{
boost::mutex::scoped_lock lock(enumerationsMutex_);
dictMetadataType_.Clear();
dictContentType_.Clear();
dictMetadataType_.Add(MetadataType_Instance_IndexInSeries, "IndexInSeries");
dictMetadataType_.Add(MetadataType_Instance_ReceptionDate, "ReceptionDate");
dictMetadataType_.Add(MetadataType_RemoteAet, "RemoteAET");
dictMetadataType_.Add(MetadataType_Series_ExpectedNumberOfInstances, "ExpectedNumberOfInstances");
dictMetadataType_.Add(MetadataType_ModifiedFrom, "ModifiedFrom");
dictMetadataType_.Add(MetadataType_AnonymizedFrom, "AnonymizedFrom");
dictMetadataType_.Add(MetadataType_LastUpdate, "LastUpdate");
dictMetadataType_.Add(MetadataType_Instance_Origin, "Origin");
dictMetadataType_.Add(MetadataType_Instance_TransferSyntax, "TransferSyntax");
dictMetadataType_.Add(MetadataType_Instance_SopClassUid, "SopClassUid");
dictMetadataType_.Add(MetadataType_Instance_RemoteIp, "RemoteIP");
dictMetadataType_.Add(MetadataType_Instance_CalledAet, "CalledAET");
dictMetadataType_.Add(MetadataType_Instance_HttpUsername, "HttpUsername");
dictMetadataType_.Add(MetadataType_Instance_PixelDataOffset, "PixelDataOffset");
dictMetadataType_.Add(MetadataType_MainDicomTagsSignature, "MainDicomTagsSignature");
dictMetadataType_.Add(MetadataType_MainDicomSequences, "MainDicomSequences");
dictMetadataType_.Add(MetadataType_Instance_PixelDataVR, "PixelDataVR");
dictContentType_.Add(FileContentType_Dicom, "dicom");
dictContentType_.Add(FileContentType_DicomAsJson, "dicom-as-json");
dictContentType_.Add(FileContentType_DicomUntilPixelData, "dicom-until-pixel-data");
}
void RegisterUserMetadata(int metadata,
const std::string& name)
{
boost::mutex::scoped_lock lock(enumerationsMutex_);
MetadataType type = static_cast<MetadataType>(metadata);
if (metadata < 0 ||
!IsUserMetadata(type))
{
LOG(ERROR) << "A user content type must have index between "
<< static_cast<int>(MetadataType_StartUser) << " and "
<< static_cast<int>(MetadataType_EndUser) << ", but \""
<< name << "\" has index " << metadata;
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
if (dictMetadataType_.Contains(type))
{
LOG(ERROR) << "Cannot associate user content type \""
<< name << "\" with index " << metadata
<< ", as this index is already used";
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
dictMetadataType_.Add(type, name);
}
std::string EnumerationToString(MetadataType type)
{
// This function MUST return a "std::string" and not "const
// char*", as the result is not a static string
boost::mutex::scoped_lock lock(enumerationsMutex_);
return dictMetadataType_.Translate(type);
}
MetadataType StringToMetadata(const std::string& str)
{
boost::mutex::scoped_lock lock(enumerationsMutex_);
return dictMetadataType_.Translate(str);
}
void GetRegisteredUserMetadata(std::map<std::string, int>& allEntries)
{
boost::mutex::scoped_lock lock(enumerationsMutex_);
allEntries.clear();
std::map<std::string, MetadataType> allEntriesTyped = dictMetadataType_.GetAllEntries();
for (std::map<std::string, MetadataType>::const_iterator it = allEntriesTyped.begin(); it != allEntriesTyped.end(); ++it)
{
if (it->second >= MetadataType_StartUser)
{
allEntries[it->first] = it->second;
}
}
}
void RegisterUserContentType(int contentType,
const std::string& name,
const std::string& mime)
{
boost::mutex::scoped_lock lock(enumerationsMutex_);
FileContentType type = static_cast<FileContentType>(contentType);
if (contentType < 0 ||
!IsUserContentType(type))
{
LOG(ERROR) << "A user content type must have index between "
<< static_cast<int>(FileContentType_StartUser) << " and "
<< static_cast<int>(FileContentType_EndUser) << ", but \""
<< name << "\" has index " << contentType;
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
if (dictContentType_.Contains(type))
{
LOG(ERROR) << "Cannot associate user content type \""
<< name << "\" with index " << contentType
<< ", as this index is already used";
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
dictContentType_.Add(type, name);
mimeTypes_[type] = mime;
}
std::string EnumerationToString(FileContentType type)
{
// This function MUST return a "std::string" and not "const
// char*", as the result is not a static string
boost::mutex::scoped_lock lock(enumerationsMutex_);
return dictContentType_.Translate(type);
}
std::string GetFileContentMime(FileContentType type)
{
if (type >= FileContentType_StartUser &&
type <= FileContentType_EndUser)
{
boost::mutex::scoped_lock lock(enumerationsMutex_);
MimeTypes::const_iterator it = mimeTypes_.find(type);
if (it != mimeTypes_.end())
{
return it->second;
}
}
switch (type)
{
case FileContentType_Dicom:
return EnumerationToString(MimeType_Dicom);
case FileContentType_DicomAsJson:
return MIME_JSON_UTF8;
default:
return EnumerationToString(MimeType_Binary);
}
}
FileContentType StringToContentType(const std::string& str)
{
boost::mutex::scoped_lock lock(enumerationsMutex_);
return dictContentType_.Translate(str);
}
FindStorageAccessMode StringToFindStorageAccessMode(const std::string& value)
{
if (value == "Always")
{
return FindStorageAccessMode_DiskOnLookupAndAnswer;
}
else if (value == "Never")
{
return FindStorageAccessMode_DatabaseOnly;
}
else if (value == "Answers")
{
return FindStorageAccessMode_DiskOnAnswer;
}
else
{
throw OrthancException(ErrorCode_ParameterOutOfRange,
"Configuration option \"StorageAccessOnFind\" "
"should be \"Always\", \"Never\" or \"Answers\": " + value);
}
}
bool IsStorageAccessAllowedForAnswers(FindStorageAccessMode mode)
{
return mode != FindStorageAccessMode_DatabaseOnly;
}
bool IsStorageAccessAllowedForLookup(FindStorageAccessMode mode)
{
return mode == FindStorageAccessMode_DiskOnLookupAndAnswer;
}
MaxStorageMode StringToMaxStorageMode(const std::string& value)
{
if (value == "Recycle")
{
return MaxStorageMode_Recycle;
}
else if (value == "Reject")
{
return MaxStorageMode_Reject;
}
else
{
throw OrthancException(ErrorCode_ParameterOutOfRange,
"Configuration option \"MaxStorageMode\" "
"should be \"Recycle\" or \"Reject\": " + value);
}
}
BuiltinDecoderTranscoderOrder StringToBuiltinDecoderTranscoderOrder(const std::string& value)
{
if (value == "Before")
{
return BuiltinDecoderTranscoderOrder_Before;
}
else if (value == "After")
{
return BuiltinDecoderTranscoderOrder_After;
}
else if (value == "Disabled")
{
return BuiltinDecoderTranscoderOrder_Disabled;
}
else
{
throw OrthancException(ErrorCode_ParameterOutOfRange,
"Configuration option \"BuiltinDecoderTranscoderOrder\" "
"should be \"After\", \"Before\" or \"Disabled\": " + value);
}
}
Verbosity StringToVerbosity(const std::string& str)
{
if (str == "default")
{
return Verbosity_Default;
}
else if (str == "verbose")
{
return Verbosity_Verbose;
}
else if (str == "trace")
{
return Verbosity_Trace;
}
else
{
throw OrthancException(ErrorCode_ParameterOutOfRange,
"Verbosity can be \"default\", \"verbose\" or \"trace\": " + str);
}
}
std::string GetBasePath(ResourceType type,
const std::string& publicId)
{
switch (type)
{
case ResourceType_Patient:
return "/patients/" + publicId;
case ResourceType_Study:
return "/studies/" + publicId;
case ResourceType_Series:
return "/series/" + publicId;
case ResourceType_Instance:
return "/instances/" + publicId;
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
}
const char* EnumerationToString(SeriesStatus status)
{
switch (status)
{
case SeriesStatus_Complete:
return "Complete";
case SeriesStatus_Missing:
return "Missing";
case SeriesStatus_Inconsistent:
return "Inconsistent";
case SeriesStatus_Unknown:
return "Unknown";
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
}
const char* EnumerationToString(StoreStatus status)
{
switch (status)
{
case StoreStatus_Success:
return "Success";
case StoreStatus_AlreadyStored:
return "AlreadyStored";
case StoreStatus_Failure:
return "Failure";
case StoreStatus_FilteredOut:
return "FilteredOut";
case StoreStatus_StorageFull:
return "StorageFull";
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
}
const char* EnumerationToString(ChangeType type)
{
switch (type)
{
case ChangeType_CompletedSeries:
return "CompletedSeries";
case ChangeType_NewInstance:
return "NewInstance";
case ChangeType_NewPatient:
return "NewPatient";
case ChangeType_NewSeries:
return "NewSeries";
case ChangeType_NewStudy:
return "NewStudy";
case ChangeType_AnonymizedStudy:
return "AnonymizedStudy";
case ChangeType_AnonymizedSeries:
return "AnonymizedSeries";
case ChangeType_ModifiedStudy:
return "ModifiedStudy";
case ChangeType_ModifiedSeries:
return "ModifiedSeries";
case ChangeType_AnonymizedPatient:
return "AnonymizedPatient";
case ChangeType_ModifiedPatient:
return "ModifiedPatient";
case ChangeType_StablePatient:
return "StablePatient";
case ChangeType_StableStudy:
return "StableStudy";
case ChangeType_StableSeries:
return "StableSeries";
case ChangeType_Deleted:
return "Deleted";
case ChangeType_NewChildInstance:
return "NewChildInstance";
case ChangeType_UpdatedAttachment:
return "UpdatedAttachment";
case ChangeType_UpdatedMetadata:
return "UpdatedMetadata";
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
}
ChangeType StringToChangeType(const std::string& value)
{
if (value == "CompletedSeries")
{
return ChangeType_CompletedSeries;
}
else if (value == "NewInstance")
{
return ChangeType_NewInstance;
}
else if (value == "NewPatient")
{
return ChangeType_NewPatient;
}
else if (value == "NewSeries")
{
return ChangeType_NewSeries;
}
else if (value == "NewStudy")
{
return ChangeType_NewStudy;
}
else if (value == "AnonymizedStudy")
{
return ChangeType_AnonymizedStudy;
}
else if (value == "AnonymizedSeries")
{
return ChangeType_AnonymizedSeries;
}
else if (value == "ModifiedStudy")
{
return ChangeType_ModifiedStudy;
}
else if (value == "ModifiedSeries")
{
return ChangeType_ModifiedSeries;
}
else if (value == "AnonymizedPatient")
{
return ChangeType_AnonymizedPatient;
}
else if (value == "ModifiedPatient")
{
return ChangeType_ModifiedPatient;
}
else if (value == "StablePatient")
{
return ChangeType_StablePatient;
}
else if (value == "StableStudy")
{
return ChangeType_StableStudy;
}
else if (value == "StableSeries")
{
return ChangeType_StableSeries;
}
else if (value == "Deleted")
{
return ChangeType_Deleted;
}
else if (value == "NewChildInstance")
{
return ChangeType_NewChildInstance;
}
else if (value == "UpdatedAttachment")
{
return ChangeType_UpdatedAttachment;
}
else if (value == "UpdatedMetadata")
{
return ChangeType_UpdatedMetadata;
}
else
{
throw OrthancException(ErrorCode_ParameterOutOfRange, "Invalid value for a change: " + value);
}
}
const char* EnumerationToString(Verbosity verbosity)
{
switch (verbosity)
{
case Verbosity_Default:
return "default";
case Verbosity_Verbose:
return "verbose";
case Verbosity_Trace:
return "trace";
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
}
bool IsUserMetadata(MetadataType metadata)
{
return (metadata >= MetadataType_StartUser &&
metadata <= MetadataType_EndUser);
}
void GetTransferSyntaxGroup(std::set<DicomTransferSyntax>& target,
TransferSyntaxGroup source)
{
target.clear();
switch (source)
{
// Transfer syntaxes supported since Orthanc 0.7.2
case TransferSyntaxGroup_Deflated:
target.insert(DicomTransferSyntax_DeflatedLittleEndianExplicit);
break;
case TransferSyntaxGroup_Jpeg:
target.insert(DicomTransferSyntax_JPEGProcess1);
target.insert(DicomTransferSyntax_JPEGProcess2_4);
target.insert(DicomTransferSyntax_JPEGProcess3_5);
target.insert(DicomTransferSyntax_JPEGProcess6_8);
target.insert(DicomTransferSyntax_JPEGProcess7_9);
target.insert(DicomTransferSyntax_JPEGProcess10_12);
target.insert(DicomTransferSyntax_JPEGProcess11_13);
target.insert(DicomTransferSyntax_JPEGProcess14);
target.insert(DicomTransferSyntax_JPEGProcess15);
target.insert(DicomTransferSyntax_JPEGProcess16_18);
target.insert(DicomTransferSyntax_JPEGProcess17_19);
target.insert(DicomTransferSyntax_JPEGProcess20_22);
target.insert(DicomTransferSyntax_JPEGProcess21_23);
target.insert(DicomTransferSyntax_JPEGProcess24_26);
target.insert(DicomTransferSyntax_JPEGProcess25_27);
target.insert(DicomTransferSyntax_JPEGProcess28);
target.insert(DicomTransferSyntax_JPEGProcess29);
target.insert(DicomTransferSyntax_JPEGProcess14SV1);
break;
case TransferSyntaxGroup_Jpeg2000:
target.insert(DicomTransferSyntax_JPEG2000);
target.insert(DicomTransferSyntax_JPEG2000LosslessOnly);
target.insert(DicomTransferSyntax_JPEG2000Multicomponent);
target.insert(DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly);
break;
case TransferSyntaxGroup_JpegLossless:
target.insert(DicomTransferSyntax_JPEGLSLossless);
target.insert(DicomTransferSyntax_JPEGLSLossy);
break;
case TransferSyntaxGroup_Jpip:
target.insert(DicomTransferSyntax_JPIPReferenced);
target.insert(DicomTransferSyntax_JPIPReferencedDeflate);
break;
case TransferSyntaxGroup_Mpeg2:
target.insert(DicomTransferSyntax_MPEG2MainProfileAtMainLevel);
target.insert(DicomTransferSyntax_MPEG2MainProfileAtHighLevel);
break;
case TransferSyntaxGroup_Rle:
target.insert(DicomTransferSyntax_RLELossless);
break;
case TransferSyntaxGroup_Mpeg4:
// New in Orthanc 1.6.0
target.insert(DicomTransferSyntax_MPEG4BDcompatibleHighProfileLevel4_1);
target.insert(DicomTransferSyntax_MPEG4HighProfileLevel4_1);
target.insert(DicomTransferSyntax_MPEG4HighProfileLevel4_2_For2DVideo);
target.insert(DicomTransferSyntax_MPEG4HighProfileLevel4_2_For3DVideo);
target.insert(DicomTransferSyntax_MPEG4StereoHighProfileLevel4_2);
break;
case TransferSyntaxGroup_H265:
// New in Orthanc 1.9.0
target.insert(DicomTransferSyntax_HEVCMainProfileLevel5_1);
target.insert(DicomTransferSyntax_HEVCMain10ProfileLevel5_1);
break;
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
}
ResponseContentFlags StringToResponseContent(const std::string& value)
{
if (value == "MainDicomTags")
{
return ResponseContentFlags_MainDicomTags;
}
else if (value == "RequestedTags")
{
return ResponseContentFlags_RequestedTags;
}
else if (value == "Metadata")
{
return ResponseContentFlags_Metadata;
}
else if (value == "Status")
{
return ResponseContentFlags_Status;
}
else if (value == "Parent")
{
return ResponseContentFlags_Parent;
}
else if (value == "Children")
{
return ResponseContentFlags_Children;
}
else if (value == "Labels")
{
return ResponseContentFlags_Labels;
}
else if (value == "Attachments")
{
return ResponseContentFlags_Attachments;
}
else if (value == "IsStable")
{
return ResponseContentFlags_IsStable;
}
else if (value == "IsProtected")
{
return ResponseContentFlags_IsProtected;
}
else
{
throw OrthancException(ErrorCode_ParameterOutOfRange,
"Unrecognized value for \"ResponseContent\": " + value);
}
}
}