/* * * Copyright (C) 2013-2014, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by * * OFFIS e.V. * R&D Division Health * Escherweg 2 * D-26121 Oldenburg, Germany * * * Module: dcmnet * * Author: Joerg Riesmeier * * Purpose: DICOM Storage Service Class Provider (SCP) * */ #ifndef DSTORSCP_H #define DSTORSCP_H #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "dcmtk/ofstd/offname.h" /* for OFFilenameCreator */ #include "dcmtk/dcmnet/scp.h" /* for base class DcmSCP */ /*---------------------* * class declaration * *---------------------*/ /** Interface class for a Storage Service Class Provider (SCP). * This class supports C-STORE and C-ECHO messages as an SCP. The received datasets are * always stored as DICOM files with the same Transfer Syntax as used for the network * transmission. Both the generation of the directory structure and the filenames can * be configured by the user. * @note The current implementation always requires to load a so-called association * negotiation profile from a configuration file, which specifies the list of * Presentation Contexts (i.e. combination of SOP Class and Transfer Syntaxes) to be * supported by this Storage SCP. If no association negotiation profile is loaded, * the SCP will only support the Verification SOP Class (with Default Transfer Syntax, * i.e. C-ECHO with Implicit VR Little Endian), which is required for all DICOM * Service Class Providers. */ class DCMTK_DCMNET_EXPORT DcmStorageSCP : public DcmSCP { public: // --- public types --- /** modes for generating subdirectories */ enum E_DirectoryGenerationMode { /// do not generate any subdirectories DGM_NoSubdirectory, /// generated subdirectories based on the Series Date (0008,0021) DGM_SeriesDate, /// default value DGM_Default = DGM_NoSubdirectory }; /** modes for generating filenames */ enum E_FilenameGenerationMode { /// generate filename from SOP Instance UID (0008,0018) FGM_SOPInstanceUID, /// generate unique filename based on new UID FGM_UniqueFromNewUID, /// generate short pseudo-random unique filename FGM_ShortUniquePseudoRandom, /// generate filename from current system time FGM_CurrentSystemTime, /// default value FGM_Default = FGM_SOPInstanceUID }; /** modes specifying whether and how to store the received datasets */ enum E_DatasetStorageMode { /// receive dataset in memory, perform some conversions and store it to file DGM_StoreToFile, /// receive dataset directly to file, i.e. write data exactly as received DGM_StoreBitPreserving, /// receive dataset in memory, but do not store it to file DSM_Ignore, /// default value DSM_Default = DGM_StoreToFile }; // --- public methods --- /** default constructor */ DcmStorageSCP(); /** destructor */ virtual ~DcmStorageSCP(); /** clear the internal member variables, i.e.\ set them to their default values */ virtual void clear(); // get methods /** get the output directory to be used for the storage of the received DICOM * datasets. Depending on the current mode for generating subdirectories (see * getDirectoryGenerationMode()), further substructures are created automatically. * @return name of the output directory that is used for storing the received * DICOM datasets */ const OFString &getOutputDirectory() const; /** get the mode for generating subdirectories used to store the received datasets * @return current mode for generating subdirectories * (see DcmStorageSCP::E_DirectoryGenerationMode) */ E_DirectoryGenerationMode getDirectoryGenerationMode() const; /** get the mode for generating filenames for the received datasets * @return current mode for generating filenames * (see DcmStorageSCP::E_FilenameGenerationMode) */ E_FilenameGenerationMode getFilenameGenerationMode() const; /** get the filename extension that is appended to the generated filenames * @return current filename extension that is appended to the generated filenames */ const OFString &getFilenameExtension() const; /** get the mode specifying whether and how to store the received datasets * @return current mode specifying whether and how to store the received datasets * (see DcmStorageSCP::E_DatasetStorageMode) */ E_DatasetStorageMode getDatasetStorageMode() const; // set methods /** specify the output directory to be used for the storage of the received DICOM * datasets. Depending on the current mode for generating subdirectories (see * getDirectoryGenerationMode()), further substructures are created automatically. * Before setting the new directory name, it is checked whether the specified * directory exists and is writable. By default, the current directory is used. * @param directory name of the output directory to be used * @return status, EC_Normal if successful, an error code otherwise */ OFCondition setOutputDirectory(const OFString &directory); /** set the mode for generating subdirectories used to store the received datasets. * These subdirectories are created below the specified output directory. * The default value is specified by DcmStorageSCP::DGM_Default. * @param mode mode to be set for generating subdirectories * (see DcmStorageSCP::E_DirectoryGenerationMode) */ void setDirectoryGenerationMode(const E_DirectoryGenerationMode mode); /** set the mode for generating filenames for the received datasets. * The default value is specified by DcmStorageSCP::FGM_Default. * @param mode mode to be set for generating filenames * (see DcmStorageSCP::E_FilenameGenerationMode) */ void setFilenameGenerationMode(const E_FilenameGenerationMode mode); /** specify the filename extension to be appended to the generated filenames. * The default value is specified by DcmStorageSCP::DEF_FilenameExtension * (empty string). A typical non-empty value would be ".dcm" (i.e. that "." * has to be specified explicitly). * @param extension filename extension appended to the generated filenames */ void setFilenameExtension(const OFString &extension); /** set the mode specifying how to store the received datasets. This mode also * allows for specifying whether to store the received datasets at all. * The default value is specified by DcmStorageSCP::DSM_Default. * @param mode mode to be set specifying whether and how to store the received * datasets (see DcmStorageSCP::E_DatasetStorageMode) */ void setDatasetStorageMode(const E_DatasetStorageMode mode); // other methods /** load an association negotiation profile from a configuration file. This profile * specifies which Presentation Contexts (i.e. combination of SOP Class and Transfer * Syntaxes) are supported by this Storage SCP. If no association negotiation * profile is loaded, the SCP will only support the Verification SOP Class (with * Default Transfer Syntax, i.e. C-ECHO with Implicit VR Litte Endian), which is * required for all DICOM Service Class Providers. * The format and semantics of this configuration file are described in * \ref file_asconfig. * @param filename filename of the configuration file to be loaded * @param profile name of the profile specified in the configuration file to be * used * @return status, EC_Normal if successful, an error code otherwise */ OFCondition loadAssociationConfiguration(const OFString &filename, const OFString &profile); protected: /** handler that is called for each incoming command message. This handler supports * C-ECHO and C-STORE requests. All other messages will be reported as an error. * After a valid C-STORE request has been received, the request and associated * dataset will be checked and further processed by checkAndProcessSTORERequest(). * @param incomingMsg pointer to data structure containing the DIMSE message * @param presInfo additional information on the Presentation Context used * @return status, EC_Normal if successful, an error code otherwise */ virtual OFCondition handleIncomingCommand(T_DIMSE_Message *incomingMsg, const DcmPresentationContextInfo &presInfo); /** check the given C-STORE request and dataset for validity. This method is called * by handleIncomingCommand() before sending the response in order to determine the * DIMSE status code to be used for the response message. If this check has been * passed successfully, the received dataset is stored as a DICOM file. * @param reqMessage C-STORE request message data structure to be checked and * processed * @param fileformat DICOM fileformat structure containing the C-STORE request * dataset to be checked and processed * @return DIMSE status code to be used for the C-STORE response */ virtual Uint16 checkAndProcessSTORERequest(const T_DIMSE_C_StoreRQ &reqMessage, DcmFileFormat &fileformat); /** generate a directory and file name for a DICOM dataset that will be received. * The naming scheme can be specified by the methods setDirectoryGenerationMode(), * setFilenameGenerationMode() and setFilenameExtension(). * Please note that this method also creates the directory structure (if needed). * @param reqMessage C-STORE request message data structure used to generate the * filename (depending on the specified options) * @param filename reference to variable that will store the resulting filename * @return status, EC_Normal if successful, an error code otherwise */ virtual OFCondition generateSTORERequestFilename(const T_DIMSE_C_StoreRQ &reqMessage, OFString &filename); /** notification handler that is called for each DICOM object that has been received * with a C-STORE request and stored as a DICOM file * @param filename filename (with full path) of the object stored * @param sopClassUID SOP Class UID of the object stored * @param sopInstanceUID SOP Instance UID of the object stored * @param dataset pointer to dataset of the object stored (or NULL if the * dataset has been stored directly to file). * Please note that this dataset will be deleted by the calling * method, so do not store any references to it! */ virtual void notifyInstanceStored(const OFString &filename, const OFString &sopClassUID, const OFString &sopInstanceUID, DcmDataset *dataset = NULL) const; /** generate a directory and file name for a DICOM dataset that has been received. * The naming scheme can be specified by the methods setDirectoryGenerationMode(), * setFilenameGenerationMode() and setFilenameExtension(). * Please note that this method only generates the names but neither creates the * directory structure nor the DICOM file. * @param filename reference to variable that will store the resulting * filename * @param directoryName reference to variable that will store the resulting * directory name (including the main output directory) * @param sopClassUID SOP Class UID of the DICOM object. This is both an * input and output parameter. If an empty value is passed * to this method, the value of the data element SOP Class * UID (0008,0016) is determined from the DICOM dataset. * @param sopInstanceUID SOP Instance UID of the DICOM object. This is both an * input and output parameter. If an empty value is passed * to this method, the value of the data element SOP * Instance UID (0008,0018) is determined from the dataset. * @param dataset pointer to dataset for which the directory and file name * is to be generated (optional) * @return status, EC_Normal if successful, an error code otherwise */ virtual OFCondition generateDirAndFilename(OFString &filename, OFString &directoryName, OFString &sopClassUID, OFString &sopInstanceUID, DcmDataset *dataset = NULL); // --- public constants --- /// default value for the name of the subdirectory that might be used for the /// "normal" case static const char *DEF_StandardSubdirectory; /// default value for the name of the subdirectory that might be used for the /// "exceptional" case static const char *DEF_UndefinedSubdirectory; /// default value for the filename extension appended to the generated filenames static const char *DEF_FilenameExtension; private: /// name of the output directory that is used to store the received datasets OFString OutputDirectory; /// name of the subdirectory that might be used for the "normal" case, i.e.\ if the /// name of the subdirectory could be generated according to the current mode OFString StandardSubdirectory; /// name of the subdirectory that might be used for the "exceptional" case, i.e.\ if /// the name of the subdirectory could not be generated according to the current mode OFString UndefinedSubdirectory; /// filename extension appended to the generated filenames OFString FilenameExtension; /// mode that is used to generate subdirectories to store the received datasets E_DirectoryGenerationMode DirectoryGeneration; /// mode that is used to generate filenames for the received datasets E_FilenameGenerationMode FilenameGeneration; /// unique pseudo-random filename creator, which also checks for existing files OFFilenameCreator FilenameCreator; /// mode specifying how to store the received datasets (also allows for skipping the storage) E_DatasetStorageMode DatasetStorage; // private undefined copy constructor DcmStorageSCP(const DcmStorageSCP &); // private undefined assignment operator DcmStorageSCP &operator=(const DcmStorageSCP &); }; #endif // DSTORSCP_H