DYT/Tool/3rdParty_x64/include/dcmtk/dcmnet/dstorscu.h

541 lines
29 KiB
C
Raw Normal View History

2024-11-22 15:19:31 +00:00
/*
*
* Copyright (C) 2011-2016, 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 User (SCU)
*
*/
#ifndef DSTORSCU_H
#define DSTORSCU_H
#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
#include "dcmtk/dcmnet/scu.h" /* for base class DcmSCU */
/*---------------------*
* class declaration *
*---------------------*/
/** Interface class for a Storage Service Class User (SCU).
* This class supports C-STORE messages as an SCU. In a first step, the SOP instances to be
* sent are added to a transfer list. In a second step, the association negotiation takes
* place where the required presentation contexts are proposed, i.e. it is checked which SOP
* classes and transfer syntaxes are needed for transferring the SOP instances. Finally, the
* SOP instances are sent to the SCP (if possible).
* \note
* \li The current implementation does not sort the transfer list according to the SOP
* Class UID and Transfer Syntax UID of the SOP instances and, therefore, might propose
* more presentation contexts than required for the transfer of all SOP instances. A
* simple optimization that is performed internally is to check whether the current SOP
* instance can be sent using a presentation context that has previously been added for
* another SOP instance (of the same kind). This approach also makes sure that studies
* and series are not mixed up, assuming that they have been added to the transfer list
* in the "correct" order.
* \li Another limitation of the current implementation is the handling of the "Default
* Transfer Syntax" in case of compression. According to the DICOM standard, the
* default transfer syntax for "Lossless JPEG Compression", "Lossy JPEG Compression"
* and so on has to be proposed in at least one presentation context for the particular
* SOP class. This is not (yet) implemented since the re-encoding of compressed
* datasets is not supported. Nevertheless, depending on the options used, the default
* transfer syntax for the uncompressed case is always proposed (if possible).
*/
class DCMTK_DCMNET_EXPORT DcmStorageSCU
: public DcmSCU
{
public:
/** dataset decompression modes
*/
enum E_DecompressionMode
{
/// never decompress datasets
DM_never,
/// decompress lossless only
DM_losslessOnly,
/// default value: decompress lossless only
DM_default = DM_losslessOnly,
/// decompress both lossy and lossless
DM_lossyAndLossless
};
/** dataset handling modes
*/
enum E_HandlingMode
{
/// do nothing with the dataset
HM_doNothing,
/// compact the dataset after it has been sent
HM_compactAfterSend,
/// delete the dataset after it has been sent
HM_deleteAfterSend,
/// delete the dataset after it has been removed from the transfer list
HM_deleteAfterRemove
};
/** default constructor
*/
DcmStorageSCU();
/** destructor
*/
virtual ~DcmStorageSCU();
/** clear the internal member variables
*/
virtual void clear();
/** get value of the association counter. This counter is initialized with 0 (that means
* no association exists) and increased by 1 for every new association. The counter is
* only reset when clear() is called.
* @return value of the association counter
*/
unsigned long getAssociationCounter() const;
/** get number of SOP instances stored in the transfer list
* @return number of SOP instances stored in the transfer list
*/
size_t getNumberOfSOPInstances() const;
/** get number of SOP instances that are to be sent (i.e.\ that are not yet sent). Please
* note that internally the transfer list is iterated to determine this number, so
* depending on the size of the list it might take some time.
* @return number of SOP instances that are to be sent
*/
size_t getNumberOfSOPInstancesToBeSent() const;
/** get mode that specifies whether or not compressed datasets are decompressed if needed,
* i.e.\ whether the transfer syntax of the dataset is changed for network transmission.
* @return decompression mode. See definition of DcmStorageSCU::E_DecompressionMode for
* possible values.
*/
E_DecompressionMode getDecompressionMode() const;
/** get mode that specifies whether to halt if an invalid file is encountered during batch
* processing (e.g.\ when adding SOP instances from a DICOMDIR) or whether to continue
* with the next SOP instance.
* @return mode indicating whether to halt on invalid file or not
*/
OFBool getHaltOnInvalidFileMode() const;
/** get mode that specifies whether to halt if unsuccessful store encountered or whether
* to continue with the next SOP instance.
* @return mode indicating whether to halt on unsuccessful store or not
*/
OFBool getHaltOnUnsuccessfulStoreMode() const;
/** get mode that specifies whether to propose presentation contexts that do not contain
* the default transfer syntax although it is needed, which might result in a violation
* of the DICOM standard.
* @return mode indicating whether illegal proposals are allowed or not
*/
OFBool getAllowIllegalProposalMode() const;
/** get mode that specifies whether to read information on SOP instances to be sent from
* the DICOMDIR files that are added to the transfer list.
* @return mode indicating whether to read from DICOMDIR files or not
*/
OFBool getReadFromDICOMDIRMode() const;
/** get C-MOVE originator information (if set)
* @param aeTitle the AE title of the originating C-MOVE client. Empty if not set.
* @param messageID the message ID used within the originating C-MOVE request. 0 if
* not set.
* @return OFTrue if either of both parameters is set
*/
OFBool getMOVEOriginatorInfo(OFString &aeTitle,
Uint16 &messageID) const;
/** set mode that specifies whether or not compressed datasets are decompressed if needed,
* i.e.\ whether the transfer syntax of the dataset is changed for network transmission.
* @param decompressionMode decompression mode. See definition of
* DcmStorageSCU::E_DecompressionMode for both possible values
* and the default value.
*/
void setDecompressionMode(const E_DecompressionMode decompressionMode);
/** set mode that specifies whether to halt if an invalid file is encountered during batch
* processing (e.g.\ when adding SOP instances from a DICOMDIR) or whether to continue
* with the next SOP instance.
* @param haltMode mode indicating whether to halt or not (default: OFTrue, i.e.\ halt)
*/
void setHaltOnInvalidFileMode(const OFBool haltMode);
/** set mode that specifies whether to halt if unsuccessful store encountered or whether
* to continue with the next SOP instance.
* @param haltMode mode indicating whether to halt or not (default: OFTrue, i.e.\ halt)
*/
void setHaltOnUnsuccessfulStoreMode(const OFBool haltMode);
/** set mode that specifies whether to propose presentation contexts that do not contain
* the default transfer syntax, although it is needed, which might result in a violation
* of the DICOM standard. For example, if a lossless compressed SOP instance is to be
* sent, there should be at least one presentation context for this SOP class that also
* proposes the default transfer syntax (Implicit VR Little Endian).
* @param allowMode mode indicating whether illegal proposals are allowed or not
* (default: OFTrue, i.e.\ allowed)
*/
void setAllowIllegalProposalMode(const OFBool allowMode);
/** set mode that specifies whether to read information on SOP instances to be sent from
* the DICOMDIR files that are added to the transfer list. If this mode is disabled, a
* DICOMDIR file is treated like any other input file. If this mode is enabled, a
* DICOMDIR file is not added to the transfer list, but the DICOM files referenced from
* it are.
* @param readMode mode indicating whether to read from DICOMDIR files or not
* (default: OFFalse, i.e.\ do not read)
*/
void setReadFromDICOMDIRMode(const OFBool readMode);
/** set C-MOVE originator information.
* If the C-STORE operation was initiated by a client's C-MOVE request, it is possible
* to convey the C-MOVE originating information (AE title and the message ID of the
* corresponding C-MOVE message) as part of the C-STORE messages in order to inform the
* C-STORE receiver (Storage SCP) about the original sender (Move SCU).
* @param aeTitle the AE title of the originating C-MOVE client. If empty, none is
* sent.
* @param messageID message ID of the originating C-MOVE request. If 0, none is sent.
*/
void setMOVEOriginatorInfo(const OFString &aeTitle = "",
const Uint16 messageID = 0);
/** reset the sent status for all SOP instances in the transfer list. This allows for
* sending the same SOP instances again - on the same or a different association.
* @param sameAssociation flag indicating whether the same association will be used for
* the transfer as last time. If a different association will
* be used, also the presentation context IDs are set to 0
* (undefined), which means that addPresentationContexts() has
* to be called again. Please make sure that all dataset
* pointers in the transfer list are still valid, i.e. the
* datasets have not been deleted.
*/
void resetSentStatus(const OFBool sameAssociation = OFFalse);
/** remove all SOP instances from the transfer list. If an entry contains a reference to
* a DICOM dataset, this dataset is deleted if the handling mode HM_deleteAfterRemove was
* used to add it to the transfer list.
*/
void removeAllSOPInstances();
/** remove a particular SOP instance from the transfer list. If the corresponding entry
* contains a reference to a DICOM dataset, this dataset is deleted if the handling mode
* HM_deleteAfterRemove was used to add it to the transfer list.
* @param sopClassUID SOP Class UID of the SOP instance to be removed
* @param sopInstanceUID SOP Instance UID of the SOP instance to be removed
* @param allOccurrences flag specifying whether to delete all occurrences of the
* SOP instance if it has been added to the list multiple times.
* If OFFalse, only the first occurrence is removed.
* @return status, EC_Normal if successful, an error code otherwise
*/
OFCondition removeSOPInstance(const OFString &sopClassUID,
const OFString &sopInstanceUID,
const OFBool allOccurrences = OFTrue);
/** add a SOP instance stored as a DICOM file or a number of SOP instances referenced from
* a DICOMDIR to the list of instances to be transferred. Before adding a SOP instance
* to the list, it is checked for validity and conformance to the DICOM standard
* (see checkSOPInstance() for details). However, duplicate instances are not
* recognized, i.e. they are added to the list and later on transferred to the storage
* SCP when calling sendSOPInstances(). If the specified DICOM file is a DICOMDIR and
* the 'ReadFromDICOMDIRMode' is enabled, the referenced SOP instances are added to the
* transfer list (using the relevant information from the DICOMDIR) and not the DICOMDIR
* itself, which is not meant to be transferred anyway. Please note that it is not
* checked whether the referenced files really exist.
* @param filename name of the DICOM file that contains the SOP instance or name of
* the DICOMDIR file that references the SOP instances to be sent
* @param readMode read mode passed to the DcmFileFormat::loadFile() method. If
* ERM_fileOnly, only the file meta information header is loaded,
* i.e. the behavior is identical to using ERM_metaOnly.
* @param checkValues flag indicating whether to check the UID values for validity and
* conformance. If OFFalse, only empty values are rejected.
* @return status, EC_Normal if successful, an error code otherwise
*/
OFCondition addDicomFile(const OFFilename &filename,
const E_FileReadMode readMode = ERM_fileOnly,
const OFBool checkValues = OFTrue);
/** add a SOP instance from a given DICOM dataset to the list of instances to be
* transferred. Before adding the SOP instance to the list, it is checked for validity
* and conformance to the DICOM standard (see checkSOPInstance() for details). However,
* duplicate instances are not recognized, i.e. they are added to the list and later on
* transferred to the storage SCP when calling sendSOPInstances().
* @param dataset DICOM dataset that contains the SOP instance to be sent
* @param datasetXfer transfer syntax of the dataset (determined automatically if
* unknown, which is also the default)
* @param handlingMode mode specifying what to do with the dataset if no longer needed.
* HM_xxxAfterSend has no effect if the C-STORE request could not
* be sent.
* Please do not add the same dataset multiple times with a mode of
* HM_deleteAfterXXX, since it will result in deleting the same
* object multiple times!
* @param checkValues flag indicating whether to check the UID values for validity and
* conformance. If OFFalse, only empty values are rejected.
* @return status, EC_Normal if successful, an error code otherwise
*/
OFCondition addDataset(DcmDataset *dataset,
const E_TransferSyntax datasetXfer = EXS_Unknown,
const E_HandlingMode handlingMode = HM_compactAfterSend,
const OFBool checkValues = OFTrue);
/** add presentation contexts for all SOP instances in the transfer list, which were not
* yet sent (either successfully or unsuccessfully). Initially, the internal list of
* presentation contexts is cleared. Then, the transfer list is iterated and a new
* presentation context is added for each SOP instance that cannot be sent using any of
* the previously added presentation contexts. If the maximum of 128 presentation
* contexts, which can be negotiated during a single association, is reached, this method
* returns and any subsequent call adds the next bunch of presentation contexts needed.
* @return status, EC_Normal if successful, an error code otherwise. If no presentation
* contexts have been added, NET_EC_NoPresentationContextsDefined is returned. This
* code can, therefore, be used to check that all SOP instances from the transfer list
* have been negotiated and sent in previous calls.
*/
OFCondition addPresentationContexts();
/** negotiate association by using presentation contexts and parameters as defined by
* earlier method calls. If negotiation fails, there is no need to close the
* association or to do anything else with this class.
* In addition to the implementation inherited from the base class DcmSCU, this method
* also handles the case that none of the proposed presentation contexts was accepted.
* And, it also increases the association counter by 1 for each new association
* (including the ones that are not successful).
* @return status, EC_Normal if successful, an error code otherwise
*/
virtual OFCondition negotiateAssociation();
/** send SOP instances to be transferred to the specified peer. After the presentation
* contexts for the current association have been added and negotiated, the SOP instances
* that are to be sent on this association can be transferred with this method. Unless
* the corresponding mode is disabled using setHaltOnUnsuccessfulStoreMode(), the
* transfer is stopped on the first SOP instance that could not be "stored" successfully.
* Please note, however, that the DIMSE status is not checked for this purpose, i.e. the
* transfer is never stopped when the DIMSE status is different from 0x0000 (success).
* Each time a SOP instance from the transfer list has been processed, the virtual method
* notifySOPInstanceSent() is called, which can be overwritten by a derived class.
* The sending process can be stopped by overwriting shouldStopAfterCurrentSOPInstance()
* in a derived class. The sending process can be continued with the next SOP instance
* by calling sendSOPInstances() again.
* @return status, EC_Normal if successful, an error code otherwise
*/
OFCondition sendSOPInstances();
/** get some status information on the overall sending process. This text can for example
* be output to the logger (on the level at the user's option).
* @param summary reference to a string in which the summary is stored
*/
void getStatusSummary(OFString &summary) const;
/** create a text file with a detailed report on the transfer of DICOM SOP instances. In
* addition to a general header and some status information at the end (as generated by
* getStatusSummary()), a couple of basic details on each SOP instance in the transfer
* list are written to the file:
* - Consecutive number
* - Name of the DICOM file
* - SOP Instance UID
* - SOP Class UID (and associated name)
* - Transfer Syntax UID (and associated name)
* - Number of the association (that was used to transfer the instance)
* - Presentation Context ID (that was used to transfer the instance)
* - DIMSE Status (if instance was sent) or reason why it was not sent
* @param filename name of the text file in which the report is stored
* @return status, EC_Normal if successful, an error code otherwise
*/
OFCondition createReportFile(const OFString &filename) const;
protected:
/** internal class/struct for a single transfer entry
*/
struct DCMTK_DCMNET_EXPORT TransferEntry
{
/** constructor. Initializes member variables with reasonable values.
* @param filename filename of the SOP instance to be transferred
* @param readMode mode to read the given SOP instance from file
* @param sopClassUID SOP Class UID of the SOP instance to be transferred
* @param sopInstanceUID SOP Instance UID of the SOP instance to be transferred
* @param transferSyntaxUID Transfer Syntax UID of the SOP instance to be
* transferred
*/
TransferEntry(const OFFilename &filename,
const E_FileReadMode readMode,
const OFString &sopClassUID,
const OFString &sopInstanceUID,
const OFString &transferSyntaxUID);
/** constructor. Initializes member variables with reasonable values.
* @param dataset pointer to the dataset of the SOP instance to be
* transferred
* @param handlingMode mode specifying what to do with the dataset if no
* longer needed
* @param sopClassUID SOP Class UID of the SOP instance to be transferred
* @param sopInstanceUID SOP Instance UID of the SOP instance to be transferred
* @param transferSyntaxUID Transfer Syntax UID of the SOP instance to be
* transferred
*/
TransferEntry(DcmDataset *dataset,
const E_HandlingMode handlingMode,
const OFString &sopClassUID,
const OFString &sopInstanceUID,
const OFString &transferSyntaxUID);
/** destructor
*/
~TransferEntry();
/// filename of the SOP instance to be transferred (if no 'Dataset' given)
const OFFilename Filename;
/// read mode that should be used to read the given SOP instance from file
const E_FileReadMode FileReadMode;
/// dataset of the SOP instance to be transferred (if no 'Filename' given)
DcmDataset *Dataset;
/// handling mode specifying what to do with the dataset if no longer needed
const E_HandlingMode DatasetHandlingMode;
/// SOP Class UID of the SOP instance to be transferred
const OFString SOPClassUID;
/// SOP Instance UID of the SOP instance to be transferred
const OFString SOPInstanceUID;
/// Transfer Syntax UID of the SOP instance to be transferred
const OFString TransferSyntaxUID;
/// transfer syntax that was used to send this SOP instance
E_TransferSyntax NetworkTransferSyntax;
/// flag indicating whether the SOP instance is uncompressed, i.e.\ uses any of
/// the three uncompressed transfer syntaxes
OFBool Uncompressed;
/// size of the dataset of the SOP instance (in bytes, 0 = not yet determined).
/// NB: The number of bytes that are actually transferred might deviate because
/// of minor changes to the DICOM dataset when appended to the C-STORE request.
unsigned long DatasetSize;
/// association number that was used to send this SOP instance (0 = not sent)
unsigned long AssociationNumber;
/// presentation context ID to be used for sending this SOP instance
T_ASC_PresentationContextID PresentationContextID;
/// flag indicating whether the C-STORE request has been sent (OFTrue = sent)
OFBool RequestSent;
/// DIMSE status of the C-STORE response (0x0000 = success)
Uint16 ResponseStatusCode;
private:
/** initialize further member variables
*/
void Init();
// private undefined copy constructor
TransferEntry(const TransferEntry &);
// private undefined assignment operator
TransferEntry &operator=(const TransferEntry &);
};
/** add SOP instances referenced from a given DICOMDIR to the list of instances to be
* transferred. Please note that the referenced DICOM files are not loaded during this
* process. Only the relevant information contained in the DICOMDIR is used.
* @param filename name of the DICOMDIR file that contains the references to SOP
* instances to be sent
* @param readMode read mode passed to the DcmFileFormat::loadFile() method when
* reading the referenced DICOM files. Not used for the DICOMDIR.
* @param checkValues flag indicating whether to check the UID values of the SOP
* instances to be added for validity and conformance. If OFFalse,
* only empty values are rejected.
* @return status, EC_Normal if successful, an error code otherwise
*/
OFCondition addDicomFilesFromDICOMDIR(const OFFilename &filename,
const E_FileReadMode readMode,
const OFBool checkValues);
/** check given SOP Class UID, SOP Instance UID and Transfer Syntax UID for validity and
* conformance to the DICOM standard. For all UID values, the compliance with the
* requirements of the value representation "Unique Identifier" (UI) and value
* multiplicity is checked. For the SOP Class UID, it is also checked whether it is a
* known or at least a possible storage SOP class (either a standard or a private one).
* For the Transfer Syntax UID, it is checked whether it is known and generally
* supported. Further checks will be performed when the list of presentation contexts is
* created for the association negotiation.
* This method is called for each SOP instance that is added to the transfer list by one
* of the following methods: addDicomFile(), addDataset(), addDicomFilesFromDICOMDIR()
* @param sopClassUID value of the SOP Class UID to be checked
* @param sopInstanceUID value of the SOP Instance UID to be checked
* @param transferSyntaxUID value of the Transfer Syntax UID to be checked
* @param checkValues flag indicating whether to check the UID values for
* validity and conformance. If OFFalse, only empty values
* are rejected.
* @return status, EC_Normal if successful, an error code otherwise
*/
virtual OFCondition checkSOPInstance(const OFString &sopClassUID,
const OFString &sopInstanceUID,
const OFString &transferSyntaxUID,
const OFBool checkValues);
/** this method is called each time before a SOP instance is sent to a peer. Therefore,
* the transfer entry passed to this method does not yet contain all information.
* @param transferEntry reference to current transfer entry that will be processed
*/
virtual void notifySOPInstanceToBeSent(const TransferEntry &transferEntry);
/** this method is called each time after a SOP instance has been sent to a peer.
* Therefore, the transfer entry passed to this method contains current and usually
* also complete information, e.g. the DIMSE status of the C-STORE response. This
* allows for counting the number of successful and failed transfers.
* @param transferEntry reference to current transfer entry that has been processed
*/
virtual void notifySOPInstanceSent(const TransferEntry &transferEntry);
/** this method is called each time after a SOP instance is sent to a peer. If the
* return value is OFTrue, the SCU will stop the sending process after the current SOP
* instance. This could for example make sense when transferring SOP instances due to
* a C-MOVE request, which is externally canceled by a C-CANCEL message. The default
* implementation always returns OFFalse. A derived class may change this behavior.
* @return OFTrue if sending should stop after current SOP instance, OFFalse otherwise.
*/
virtual OFBool shouldStopAfterCurrentSOPInstance();
private:
/// association counter
unsigned long AssociationCounter;
/// presentation context counter
unsigned long PresentationContextCounter;
/// decompression mode, i.e.\ whether a dataset is decompressed for transmission
E_DecompressionMode DecompressionMode;
/// flag indicating whether to halt on invalid file
OFBool HaltOnInvalidFileMode;
/// flag indicating whether to halt on unsuccessful store
OFBool HaltOnUnsuccessfulStoreMode;
/// flag indicating whether to allow illegal proposals
OFBool AllowIllegalProposalMode;
/// flag indicating whether to read from DICOMDIR files
OFBool ReadFromDICOMDIRMode;
/// AE title of the C-MOVE client that initiated the C-STORE operation (if applicable)
OFString MoveOriginatorAETitle;
/// message ID of the C-MOVE message that initiated the C-STORE operation (if applicable)
Uint16 MoveOriginatorMsgID;
/// list of SOP instances to be transferred
OFList<TransferEntry *> TransferList;
/// iterator pointing to the current entry in the list of SOP instances to be transferred
OFListIterator(TransferEntry *) CurrentTransferEntry;
// private undefined copy constructor
DcmStorageSCU(const DcmStorageSCU &);
// private undefined assignment operator
DcmStorageSCU &operator=(const DcmStorageSCU &);
};
#endif // DSTORSCU_H