1 /* 2 * 3 * Copyright (C) 2008-2019, OFFIS e.V. 4 * All rights reserved. See COPYRIGHT file for details. 5 * 6 * This software and supporting documentation were developed by 7 * 8 * OFFIS e.V. 9 * R&D Division Health 10 * Escherweg 2 11 * D-26121 Oldenburg, Germany 12 * 13 * 14 * Module: dcmnet 15 * 16 * Author: Michael Onken 17 * 18 * Purpose: Base class for Service Class Users (SCUs) 19 * 20 */ 21 22 #ifndef SCU_H 23 #define SCU_H 24 25 #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ 26 27 #include "dcmtk/dcmdata/dctk.h" /* covers most common dcmdata classes */ 28 #include "dcmtk/dcmnet/dcasccff.h" /* for reading a association config file */ 29 #include "dcmtk/dcmnet/dcasccfg.h" /* for holding association config file infos */ 30 #include "dcmtk/dcmnet/dcompat.h" 31 #include "dcmtk/dcmnet/dimse.h" /* DIMSE network layer */ 32 #include "dcmtk/ofstd/oflist.h" 33 34 // include this file in doxygen documentation 35 36 /** @file scu.h 37 * @brief general Service Class User (SCU) class 38 */ 39 40 /** Different types of closing an association 41 */ 42 enum DcmCloseAssociationType 43 { 44 /// Release the current association 45 DCMSCU_RELEASE_ASSOCIATION, 46 /// Abort the current association 47 DCMSCU_ABORT_ASSOCIATION, 48 /// Peer requested release (Aborting) 49 DCMSCU_PEER_REQUESTED_RELEASE, 50 /// Peer aborted the association 51 DCMSCU_PEER_ABORTED_ASSOCIATION 52 }; 53 54 /** Storage mode used for DICOM objects received via C-STORE 55 */ 56 enum DcmStorageMode 57 { 58 /// Ignore any objects received via C-STORE 59 DCMSCU_STORAGE_IGNORE, 60 /// Try to store the objects to disk 61 DCMSCU_STORAGE_DISK, 62 /// Try to store to disk in bit-preserving mode. This is especially useful 63 /// for huge files that cannot fully be received in memory since the data 64 /// is directly streamed to disk. Originally, this was introduced for DICOM 65 /// signatures which can be kept valid this way. 66 DCMSCU_STORAGE_BIT_PRESERVING 67 }; 68 69 /** Base class for C-FIND, C-MOVE and C-GET responses 70 */ 71 class DCMTK_DCMNET_EXPORT QRResponse 72 { 73 public: 74 /** Standard constructor. 75 */ QRResponse()76 QRResponse() 77 : m_messageIDRespondedTo(0) 78 , m_affectedSOPClassUID() 79 , m_dataset(NULL) 80 , m_status(0) 81 , m_statusDetail(NULL) 82 { 83 } 84 85 /** Destructor, cleans up internal memory (dataset if present). 86 */ ~QRResponse()87 virtual ~QRResponse() 88 { 89 delete m_dataset; 90 delete m_statusDetail; 91 } 92 93 /// The message ID responded to (mandatory response field, 94 /// equals message ID from request) 95 Uint16 m_messageIDRespondedTo; 96 97 /// Optional response field according to part 7 of the standard. 98 /// If present, equals SOP Class UID from request. 99 OFString m_affectedSOPClassUID; 100 101 /// Conditional response field (NULL if absent). From the standard (2009, 102 /// part 4, C.4.2.1.4.2), for C-MOVE: In Q/R if no C-STORE sub-operation 103 /// failed, Failed SOP Instance UID List (0008,0058) is absent and 104 /// therefore no Data Set shall be sent in the C-MOVE response. Further 105 /// rules: Statuses of Canceled, Failure, Refused, or Warning shall 106 /// contain the Failed SOP Instance UID List Attribute; status of 107 /// Pending shall not. 108 DcmDataset* m_dataset; 109 110 /// The returned DIMSE status (mandatory Response Field) 111 Uint16 m_status; 112 113 /// Status detail (NULL if absent). For some DIMSE return status codes, 114 /// an additional dataset is sent which gives further information (i.e. 115 /// in case of warnings or errors). 116 DcmDataset* m_statusDetail; 117 118 private: 119 /** Private undefined copy constructor. 120 * @param other The find response to copy from 121 */ 122 QRResponse(const QRResponse& other); 123 124 /** Private undefined assignment operator. 125 * @param other The find response that should be assigned from 126 * @return Reference to this 127 */ 128 QRResponse& operator=(const QRResponse& other); 129 }; 130 131 /// Base class representing for single C-GET or C-MOVE response 132 class DCMTK_DCMNET_EXPORT RetrieveResponse : public QRResponse 133 { 134 public: 135 /** Standard constructor 136 */ RetrieveResponse()137 RetrieveResponse() 138 : m_numberOfRemainingSubops(0) 139 , m_numberOfCompletedSubops(0) 140 , m_numberOfFailedSubops(0) 141 , m_numberOfWarningSubops(0) 142 { 143 } 144 145 /** Destructor, cleans up internal memory 146 */ ~RetrieveResponse()147 virtual ~RetrieveResponse() 148 { 149 } 150 151 /** Prints response to INFO log level. 152 */ 153 void print(); 154 155 /// Number of remaining sub operations (in Q/R: C-STORE calls). 156 /// For Q/R MOVE and GET, for status of pending this field shall be filled. 157 /// For others, the field may be filled. 158 Uint16 m_numberOfRemainingSubops; 159 160 /// Number of successfully completed sub operations (in Q/R: C-STORE calls). 161 /// For Q/R MOVE and GET, for status of pending this field shall be filled. 162 /// For others, the field may be filled. 163 Uint16 m_numberOfCompletedSubops; 164 165 /// Number of failed sub operations (in Q/R: C-STORE calls). 166 /// For Q/R MOVE and GET, for status of pending this field shall be filled. 167 /// For others, the field may be filled. 168 Uint16 m_numberOfFailedSubops; 169 170 /// Number generated warnings generated by sub operations (in Q/R: C-STORE calls). 171 /// For Q/R MOVE and GET, for status of pending this field shall be filled. 172 /// For others, the field may be filled. 173 Uint16 m_numberOfWarningSubops; 174 175 private: 176 /** Private undefined copy constructor 177 * @param other Response to copy from 178 */ 179 RetrieveResponse(const RetrieveResponse& other); 180 181 /** Private undefined assignment operator 182 * @param other Response that should be assigned from 183 * @return Reference to this 184 */ 185 RetrieveResponse& operator=(const RetrieveResponse& other); 186 }; 187 188 /** Base class for implementing DICOM Service Class User functionality. The class offers 189 * support for negotiating associations and sending and receiving arbitrary DIMSE messages 190 * on that connection. DcmSCU has built-in C-ECHO support so derived classes do not have to 191 * implement that capability on their own. 192 * @warning This class is EXPERIMENTAL. Be careful to use it in production environment. 193 */ 194 class DCMTK_DCMNET_EXPORT DcmSCU 195 { 196 197 public: 198 /** Constructor, just initializes internal class members 199 */ 200 DcmSCU(); 201 202 /** Virtual destructor 203 */ 204 virtual ~DcmSCU(); 205 206 /** Add presentation context to be used for association negotiation 207 * @param abstractSyntax [in] Abstract syntax name in UID format 208 * @param xferSyntaxes [in] List of transfer syntaxes to be added for the given abstract 209 * syntax 210 * @param role [in] The role to be negotiated 211 * @return EC_Normal if adding was successful, otherwise error code 212 */ 213 OFCondition addPresentationContext(const OFString& abstractSyntax, 214 const OFList<OFString>& xferSyntaxes, 215 const T_ASC_SC_ROLE role = ASC_SC_ROLE_DEFAULT); 216 217 /** Initialize network, i.e.\ prepare for association negotiation. If the SCU is already 218 * connected, the call will not be successful and the old connection keeps open. 219 * @return EC_Normal if initialization was successful, otherwise error code. 220 * NET_EC_AlreadyConnected if SCU is already connected. 221 */ 222 virtual OFCondition initNetwork(); 223 224 /** Negotiate association by using presentation contexts and parameters as defined by 225 * earlier function calls. If negotiation fails, there is no need to close the association 226 * or to do anything else with this class. 227 * @return EC_Normal if negotiation was successful, otherwise error code. 228 * NET_EC_AlreadyConnected if SCU is already connected. 229 */ 230 virtual OFCondition negotiateAssociation(); 231 232 /** After negotiation association, this call returns the first usable presentation context 233 * given the desired abstract syntax and transfer syntax 234 * @param abstractSyntax [in] The abstract syntax (UID) to look for 235 * @param transferSyntax [in] The transfer syntax (UID) to look for. If empty, the transfer 236 * syntax is not checked. 237 * @param requestorRole [in] The role to look for (denoting the role the association 238 * requestor plays) 239 * @return Adequate Presentation context ID that can be used. 0 if none found. 240 */ 241 T_ASC_PresentationContextID findPresentationContextID(const OFString& abstractSyntax, 242 const OFString& transferSyntax, 243 const T_ASC_SC_ROLE requestorRole = ASC_SC_ROLE_DEFAULT); 244 245 /** After a successful association negotiation, this function is called to return the 246 * presentation context ID that best matches the desired abstract syntax and transfer 247 * syntax (TS). The function tries to do the following: 248 * - If possible finds a presentation context with matching TS 249 * - Else then tries to find an explicit VR uncompressed TS presentation context 250 * - Else then tries to find an implicit VR uncompressed TS presentation context 251 * - Else finally accepts each matching presentation ctx independent of TS. 252 * @warning This method does not support filtering for a specific role, yet. 253 * @param abstractSyntax [in] The abstract syntax (UID) to look for 254 * @param transferSyntax [in] The transfer syntax (UID) to look for. If empty, the transfer 255 * syntax is not checked. 256 * @return Adequate Presentation context ID that can be used. 0 if no appropriate 257 * presentation context could be found at all. 258 */ 259 T_ASC_PresentationContextID findAnyPresentationContextID(const OFString& abstractSyntax, 260 const OFString& transferSyntax); 261 262 /** This function sends a C-ECHO command via network to another DICOM application 263 * @param presID [in] Presentation context ID to use. A value of 0 lets SCP class tries 264 * to choose one on its own. 265 * @return EC_Normal if echo was successful, an error code otherwise 266 * 267 */ 268 virtual OFCondition sendECHORequest(const T_ASC_PresentationContextID presID); 269 270 /** This function sends a C-STORE request on the currently opened association and receives 271 * the corresponding response then. If required and supported, the dataset of the SOP 272 * instance can be converted automatically to the network transfer syntax that was 273 * negotiated (and is specified by the parameter 'presID'). However, this feature is 274 * disabled by default. See setDatasetConversionMode() on how to enable it. 275 * @param presID [in] Contains in the end the ID of the presentation context which 276 * was specified in the DIMSE command. If 0 is given, the 277 * function tries to find an appropriate presentation context 278 * itself (based on SOP class and original transfer syntax of 279 * the 'dicomFile' or 'dataset'). 280 * @param dicomFile [in] The filename of the DICOM file to be sent. Alternatively, a 281 * dataset can be given in the next parameter. If both are given 282 * the dataset from the file name is used. 283 * @param dataset [in] The dataset to be sent. Alternatively, a filename can be 284 * specified in the previous parameter. If both are given the 285 * dataset from the filename is used. 286 * @param rspStatusCode [out] The response status code received. 0 means success, others 287 * can be found in the DICOM standard. 288 * @param moveOriginatorAETitle [in] If this C-STORE is started due to a C-MOVE request, 289 * this parameter informs the C-STORE SCP about the C-MOVE 290 * client's AE title. 291 * @param moveOriginatorMsgID [in] If this C-STORE is started due to a C-MOVE request, 292 * this parameter informs the C-STORE SCP about the C-MOVE 293 * message ID. 294 * @return EC_Normal if request could be issued and response was received successfully, 295 * error code otherwise. That means that if the receiver sends a response denoting 296 * failure of the storage request, EC_Normal will be returned. 297 */ 298 virtual OFCondition sendSTORERequest(const T_ASC_PresentationContextID presID, 299 const OFFilename& dicomFile, 300 DcmDataset* dataset, 301 Uint16& rspStatusCode, 302 const OFString& moveOriginatorAETitle = "", 303 const Uint16 moveOriginatorMsgID = 0); 304 305 /** Sends a C-MOVE Request on given presentation context and receives list of responses. 306 * The function receives the first response and then calls the function handleMOVEResponse() 307 * which gets the relevant presentation context together with the response dataset and 308 * status information. Then it waits again for the next response, if there are more to 309 * come (i.e. response status is PENDING). In the end, after receiving all responses, the 310 * full list of responses is returned to the caller. If he is not interested, he just sets 311 * responses=NULL when calling the function. 312 * This function can be overwritten by actual SCU implementations but just should work fine 313 * for most people. 314 * @param presID [in] The presentation context ID that should be used. 315 * Must be an odd number. 316 * @param moveDestinationAETitle [in] The move destination's AE title, i.e.\ the one that 317 * is used for connection to the storage server. 318 * @param dataset [in] The dataset containing the information about the 319 * object(s) to be retrieved. 320 * @param responses [out] The incoming C-MOVE responses for this request. 321 * The caller is responsible for providing a non-NULL 322 * pointer for this case. After receiving the results, 323 * the caller is responsible for freeing the memory of 324 * this variable. If NULL is specified, the responses 325 * will not be returned to the caller. 326 * @return EC_Normal if everything went fine, i.e.\ if request could be send and responses 327 * (with whatever status) could be received. 328 */ 329 virtual OFCondition sendMOVERequest(const T_ASC_PresentationContextID presID, 330 const OFString& moveDestinationAETitle, 331 DcmDataset* dataset, 332 OFList<RetrieveResponse*>* responses); 333 334 /** This is the standard handler for C-MOVE message responses: It just adds up all responses 335 * it receives and prints a DEBUG message. Therefore, it is called for each response 336 * received in sendMOVERequest(). The idea is of course to overwrite this function in a 337 * derived, actual SCU implementation if required. Thus, after each response, the caller of 338 * sendMOVERequest() can decide on its own whether he wants to cancel the C-MOVE session, 339 * terminate the association, do something useful or whatever. Thus this function is a more 340 * object-oriented kind of callback. 341 * @param presID [in] The presentation context ID where the response was 342 * received on. 343 * @param response [in] The C-MOVE response received. 344 * @param waitForNextResponse [out] Denotes whether SCU should try to receive another 345 * response. If set to OFTrue, then sendMOVERequest() will 346 * continue waiting for responses. The current 347 * implementation does that for all responses do not have 348 * status Failed, Warning, Success or unknown. If set to 349 * OFFalse, sendMOVERequest() will return control to the 350 * caller. 351 * @return EC_Normal, if response could be handled. Error code otherwise. 352 * The current implementation always returns EC_Normal. 353 */ 354 virtual OFCondition handleMOVEResponse(const T_ASC_PresentationContextID presID, 355 RetrieveResponse* response, 356 OFBool& waitForNextResponse); 357 358 /** Sends a C-GET Request on given presentation context and receives list of responses. It 359 * then switches control to the function handleCGETSession(). 360 * The full list of responses is returned to the caller. If he is not interested, he can 361 * set responses=NULL when calling the function. 362 * This function can be overwritten by actual SCU implementations but just should work fine 363 * for most people. 364 * @param presID [in] The presentation context ID that should be used. Must be an odd 365 * number. 366 * @param dataset [in] The dataset containing the information about the 367 * object(s) to be retrieved 368 * @param responses [out] The incoming C-GET responses for this request. If the caller 369 * specifies NULL, no responses will be returned; otherwise there 370 * should be at least one final C-GET response (mandatory). C-GET 371 * responses after each DICOM object received are optional and may 372 * have been omitted by the server. 373 * @return EC_Normal if everything went fine, i.e.\ if request could be sent and expected 374 * responses (with whatever status) could be received. 375 */ 376 virtual OFCondition sendCGETRequest(const T_ASC_PresentationContextID presID, 377 DcmDataset* dataset, 378 OFList<RetrieveResponse*>* responses); 379 380 /** Does the logic for switching between C-GET Response and C-STORE Requests. Sends a C-GET 381 * Request on given presentation context and receives list of responses. The full list of 382 * responses is returned to the caller. If he is not interested, he can set responses=NULL 383 * when calling the function. After sending a C-GET Request, there might be two different 384 * responses coming in: C-GET-RSP (optional after each received object and mandatory after 385 * the last object) or a mandatory C-STORE for each incoming object that is received due to 386 * the request. This function therefore either calls handleCGETResponse() or 387 * handleSTORERequest() in order to deal with the incoming message. All other messages lead 388 * to an error within this handler. 389 * This function can be overwritten by actual SCU implementations but just should work fine 390 * for most people. 391 * @param presID [in] The presentation context ID that should be used. Must be an odd 392 * number. 393 * @param dataset [in] The dataset containing the information about the object(s) to be 394 * retrieved 395 * @param responses [out] The incoming C-GET responses for this request. If the caller 396 * specifies NULL, no responses will be returned; otherwise there 397 * should be at least one final C-GET response (mandatory). C-GET 398 * responses after each DICOM object received are optional and may 399 * have been omitted by the server. 400 * @return EC_Normal if everything went fine, i.e.\ if request could be send 401 * and expected responses (with whatever status) could be received. 402 */ 403 virtual OFCondition handleCGETSession(const T_ASC_PresentationContextID presID, 404 DcmDataset* dataset, 405 OFList<RetrieveResponse*>* responses); 406 407 /** Function handling a single C-GET Response. This standard handler reads the status of the 408 * response and decides whether to receive any further messages related to the original 409 * C-GET Request or whether the last response was received or an error occurred. 410 * @param presID [in] The presentation context the C-GET Response was 411 * received on. 412 * @param response [in] The response received 413 * @param continueCGETSession [out] Defines whether it is decided to wait for further C-GET 414 * Responses/C-STORE Requests within this C-GET session 415 * @return If no errors occur (dataset response NULL, SCU not connected), this method will 416 * return EC_Normal, otherwise error code. 417 */ 418 virtual OFCondition handleCGETResponse(const T_ASC_PresentationContextID presID, 419 RetrieveResponse* response, 420 OFBool& continueCGETSession); 421 422 /** Function handling a single C-STORE Request. If storage mode is set to disk (default), 423 * this function is called and the incoming object stored to disk. 424 * @param presID [in] The presentation context the C-STORE Request was 425 * received on. 426 * @param incomingObject [in] The dataset (the object) received. The function 427 * takes ownership of the dataset, i.e. deletes it 428 * after processing. 429 * @param continueCGETSession [out] Defines whether it is decided to wait for further 430 * C-GET Responses/C-STORE requests within this C-GET 431 * session. 432 * @param cStoreReturnStatus [out] Denotes the desired C-STORE return status. 433 * @return If errors occur (incomingObject NULL or SCU not connected or file could not be 434 * stored), this method will return an error code otherwise EC_Normal. 435 */ 436 virtual OFCondition handleSTORERequest(const T_ASC_PresentationContextID presID, 437 DcmDataset* incomingObject, 438 OFBool& continueCGETSession, 439 Uint16& cStoreReturnStatus); 440 441 /** Function handling a single C-STORE Request. If storage mode is set to bit preserving, 442 * this function is called and the incoming object stored directly to disk, i.e. not stored 443 * fully in memory. 444 * @param presID [in] The presentation context the C-STORE Response was received on. 445 * @param filename [in] The filename to store to 446 * @param request [in] The incoming C-STORE request command set 447 * @return If errors occur (incomingObject NULL or SCU not connected filename not 448 * specified), this method will return an error code otherwise EC_Normal. 449 */ 450 virtual OFCondition 451 handleSTORERequestFile(T_ASC_PresentationContextID* presID, const OFString& filename, T_DIMSE_C_StoreRQ* request); 452 453 /** Sends a C-FIND Request on given presentation context and receives list of responses. 454 * The function receives the first response and then calls the function handleFINDResponse() 455 * which gets the relevant presentation context together with the response dataset and 456 * status information. Then it waits again for the next response, if there are more to 457 * come (i.e. response status is PENDING). In the end, after receiving all responses, the 458 * full list of responses is returned to the caller. If he is not interested, he just sets 459 * responses=NULL when calling the function. 460 * This function can be overwritten by actual SCU implementations but just should work fine 461 * for most people. 462 * @param presID [in] The presentation context ID that should be used. Must be an odd 463 * number. 464 * @param queryKeys [in] The dataset containing the query keys to be searched for on the 465 * server (SCP). 466 * @param responses [out] The incoming C-FIND responses for this request. The caller is 467 * responsible for providing a non-NULL pointer for this case. 468 * After receiving the results, the caller is responsible for 469 * freeing the memory of this variable. If NULL is specified, the 470 * responses will be not returned to the caller. 471 * @return EC_Normal if everything went fine, i.e.\ if request could be send and responses 472 * (with whatever status) could be received. 473 */ 474 virtual OFCondition 475 sendFINDRequest(const T_ASC_PresentationContextID presID, DcmDataset* queryKeys, OFList<QRResponse*>* responses); 476 477 /** This is the standard handler for C-FIND message responses: It just adds up all responses 478 * it receives and prints a DEBUG message. Therefore, it is called for each response 479 * received in sendFINDRequest(). The idea is of course to overwrite this function in a 480 * derived, actual SCU implementation if required. Thus, after each response, the caller of 481 * sendFINDRequest() can decide on its own whether he wants to cancel the C-FIND session, 482 * terminate the association, do something useful or whatever. That way this is a more 483 * object-oriented kind of callback. 484 * @param presID [in] The presentation context ID where the response was 485 * received on. 486 * @param response [in] The C-FIND response received. 487 * @param waitForNextResponse [out] Denotes whether SCU should try to receive another 488 * response. If set to OFTrue, then sendFINDRequest() 489 * will continue waiting for responses. The current 490 * implementation does that for all responses do not have 491 * status SUCCESS. If set to OFFalse, sendFINDRequest() 492 * will return control to the caller. 493 * @return EC_Normal, if response could be handled. Error code otherwise. 494 * The current implementation always returns EC_Normal. 495 */ 496 virtual OFCondition 497 handleFINDResponse(const T_ASC_PresentationContextID presID, QRResponse* response, OFBool& waitForNextResponse); 498 499 /** Send C-CANCEL and, therefore, ends the C-FIND -GET or -MOVE session, i.e.\ no further 500 * responses will be handled. A call to this function only makes sense if an association 501 * is open, the given presentation context represents a valid C-FIND/GET/MOVE-enabled SOP 502 * class and usually only, if the last command send on that presentation context was a 503 * C-FIND message. 504 * @param presID [in] The presentation context ID where the C-CANCEL should be sent on. 505 * @return The current implementation always returns EC_Normal. 506 */ 507 virtual OFCondition sendCANCELRequest(const T_ASC_PresentationContextID presID); 508 509 /** This function sends a N-ACTION request on the currently opened association and receives 510 * the corresponding response then 511 * @param presID [in] The ID of the presentation context to be used for sending 512 * the request message. Should not be 0. 513 * @param sopInstanceUID [in] The requested SOP Instance UID 514 * @param actionTypeID [in] The action type ID to be used 515 * @param reqDataset [in] The dataset to be sent 516 * @param rspStatusCode [out] The response status code received. 0 means success, 517 * others can be found in the DICOM standard. 518 * @return EC_Normal if request could be issued and response was received successfully, 519 * an error code otherwise 520 */ 521 virtual OFCondition sendACTIONRequest(const T_ASC_PresentationContextID presID, 522 const OFString& sopInstanceUID, 523 const Uint16 actionTypeID, 524 DcmDataset* reqDataset, 525 Uint16& rspStatusCode); 526 527 /** This function sends N-EVENT-REPORT request and receives the corresponding response 528 * @param presID [in] The ID of the presentation context to be used for sending 529 * the request message. Should not be 0. 530 * @param sopInstanceUID [in] The requested SOP Instance UID 531 * @param eventTypeID [in] The event type ID to be used 532 * @param reqDataset [in] The request dataset to be sent 533 * @param rspStatusCode [out] The response status code received. 0 means success, 534 * others can be found in the DICOM standard. 535 * @return EC_Normal if request could be issued and response was received successfully, 536 * an error code otherwise 537 */ 538 virtual OFCondition sendEVENTREPORTRequest(const T_ASC_PresentationContextID presID, 539 const OFString& sopInstanceUID, 540 const Uint16 eventTypeID, 541 DcmDataset* reqDataset, 542 Uint16& rspStatusCode); 543 544 /** Receives N-EVENT-REPORT request on the currently opened association and sends a 545 * corresponding response. Calls checkEVENTREPORTRequest() in order to determine the 546 * DIMSE status code to be used for the N-EVENT-REPORT response. 547 * @param reqDataset [out] Pointer to the dataset received 548 * @param eventTypeID [out] Event Type ID from the command set received 549 * @param timeout [in] Optional timeout in seconds for receiving data. This value 550 * (if not 0) overwrites the standard DIMSE timeout and also 551 * enables the non-blocking mode for receiving the request. 552 * @return status, EC_Normal if successful, an error code otherwise 553 */ 554 virtual OFCondition handleEVENTREPORTRequest(DcmDataset*& reqDataset, Uint16& eventTypeID, const int timeout = 0); 555 556 /** Function handling a single C-GET, C-FIND or C-MOVE Response, used by 557 * handleCGETResponse(), handleFINDResponse() and handleMOVEResponse(). 558 * It prints a message to the logger (warning, error or debug level related 559 * to the status code provided) and sets waitForNextResponse if the status 560 * provided is a DIMSE pending status. 561 * @param dimseStatus [in] The DIMSE status code to handle 562 * @param message [in] The message to be printed 563 * @param waitForNextResponse [out] Defines whether it is decided to wait for further 564 * Responses with the C-GET/FIND/MOVE session. 565 * @return If no processing errors occurs this method will return EC_Normal, 566 * otherwise an error code. 567 */ 568 virtual OFCondition 569 handleSessionResponseDefault(const Uint16 dimseStatus, const OFString& message, OFBool& waitForNextResponse); 570 571 /** Closes the association created by this SCU. Also resets the current association. 572 * @deprecated The use of this method is deprecated. Please use releaseAssociation() 573 * or abortAssociation() instead. 574 * @param closeType [in] Define whether to release or abort the association 575 */ 576 virtual void closeAssociation(const DcmCloseAssociationType closeType); 577 578 /** Releases the current association by sending an A-RELEASE request to the SCP. 579 * Please note that this release only applies to associations that have been 580 * created by calling initNetwork() and negotiateAssociation(). 581 * @return status, EC_Normal if successful, an error code otherwise 582 */ 583 virtual OFCondition releaseAssociation(); 584 585 /** Aborts the current association by sending an A-ABORT request to the SCP. 586 * Please note that this abort only applies to associations that have been 587 * created by calling initNetwork() and negotiateAssociation(). 588 * @return status, EC_Normal if successful, an error code otherwise 589 */ 590 virtual OFCondition abortAssociation(); 591 592 /* Set methods */ 593 594 /** Set maximum PDU length (to be received by SCU) 595 * @param maxRecPDU [in] The maximum PDU size to use in bytes 596 */ 597 void setMaxReceivePDULength(const Uint32 maxRecPDU); 598 599 /** Set whether to send in DIMSE blocking or non-blocking mode 600 * @param blockingMode [in] Either blocking or non-blocking mode 601 */ 602 void setDIMSEBlockingMode(const T_DIMSE_BlockingMode blockingMode); 603 604 /** Set SCU's AE title to be used in association negotiation 605 * @param myAETtitle [in] The SCU's AE title to be used 606 */ 607 void setAETitle(const OFString& myAETtitle); 608 609 /** Set SCP's host (host name or IP address) to talk to in association negotiation 610 * @param peerHostName [in] The SCP's hostname or IP address to be used 611 */ 612 void setPeerHostName(const OFString& peerHostName); 613 614 /** Set SCP's AE title to talk to in association negotiation 615 * @param peerAETitle [in] The SCP's AE title to be used 616 */ 617 void setPeerAETitle(const OFString& peerAETitle); 618 619 /** Set SCP's port number to connect to for association negotiation 620 * @param peerPort [in] The SCP's port number 621 */ 622 void setPeerPort(const Uint16 peerPort); 623 624 /** Set timeout for receiving DIMSE messages 625 * @param dimseTimeout [in] DIMSE timeout in seconds for receiving data. 626 * If the blocking mode is DIMSE_NONBLOCKING the SCU 627 * will try to read data from the incoming socket stream 628 * for the number of seconds configured. 629 */ 630 void setDIMSETimeout(const Uint32 dimseTimeout); 631 632 /** Set timeout for receiving ACSE messages 633 * @param acseTimeout [in] ACSE timeout in seconds used by timer for message timeouts 634 * during association negotiation 635 */ 636 void setACSETimeout(const Uint32 acseTimeout); 637 638 /** Set global timeout for connecting to the SCP. Note that this is a global 639 * DCMTK setting i.e. it affects all code that uses dcmnet to start a DICOM 640 * association to another host. Setting the timeout to -1 sets an infinite timeout, 641 * i.e. any association request will wait forever (blocking) until the SCP accepts 642 * the connection request. A value of 0 lets the SCU return immediately if the SCP 643 * is not reachable at the first attempt. 644 * @param connectionTimeout [in] Connection Timeout in seconds when connecting 645 * to SCPs. -1 will wait forever (blocking mode). 646 */ 647 void setConnectionTimeout(const Sint32 connectionTimeout); 648 649 /** Set an association configuration file and profile to be used 650 * @param filename [in] File name of the association configuration file 651 * @param profile [in] Profile inside the association negotiation file 652 */ 653 void setAssocConfigFileAndProfile(const OFString& filename, const OFString& profile); 654 655 /** Set the directory that should be used by the standard C-GET handler to store objects 656 * that come in with the corresponding C-STORE requests 657 * @param storeDir [in] The directory to store to. It is checked in handleSTORERequest() 658 * whether the directory is writeable and readable. By default, the 659 * received objects are stored in the current working directory. 660 */ 661 void setStorageDir(const OFString& storeDir); 662 663 /** Set the storage mode to be used. Default is DCMSCU_STORAGE_DISK. 664 * @param storageMode The storage mode to be used. 665 */ 666 void setStorageMode(const DcmStorageMode storageMode); 667 668 /** Set whether to show presentation contexts in verbose or debug mode 669 * @param mode [in] Show presentation contexts in verbose mode if OFTrue. By default, 670 * the presentation contexts are shown in debug mode. 671 */ 672 void setVerbosePCMode(const OFBool mode); 673 674 /** Set the mode that specifies whether the transfer syntax of the dataset can be changed 675 * for network transmission. This mainly covers the compression/decompression of datasets, 676 * which is disabled by default. 677 * @param mode [in] Allow dataset conversion if OFTrue 678 */ 679 void setDatasetConversionMode(const OFBool mode); 680 681 /** Set the mode that specifies whether the progress of sending and receiving DIMSE 682 * messages is notified by calling notifySENDProgress() and notifyRECEIVEProgress(), 683 * respectively. The progress notification is enabled by default. 684 * @param mode [in] Disable progress notification if OFFalse 685 */ 686 void setProgressNotificationMode(const OFBool mode); 687 688 /* Get methods */ 689 690 /** Get current connection status 691 * @return OFTrue if SCU is currently connected, OFFalse otherwise 692 */ 693 OFBool isConnected() const; 694 695 /** Returns maximum PDU length configured to be received by SCU 696 * @return Maximum PDU length in bytes 697 */ 698 Uint32 getMaxReceivePDULength() const; 699 700 /** Returns whether DIMSE messaging is configured to be blocking or unblocking 701 * @return The blocking mode configured 702 */ 703 T_DIMSE_BlockingMode getDIMSEBlockingMode() const; 704 705 /** Returns the SCU's own configured AE title 706 * @return The AE title configured for this SCU 707 */ 708 const OFString& getAETitle() const; 709 710 /** Returns the SCP's (peer's) host configured 711 * @return The host name (or IP) configured to be talked to 712 */ 713 const OFString& getPeerHostName() const; 714 715 /** Returns the SCP's (peer's) AE title configured 716 * @return The AE title configured to be talked to 717 */ 718 const OFString& getPeerAETitle() const; 719 720 /** Returns the SCP's (peer's) TCP/IP port configured 721 * @return The port configured to talked to 722 */ 723 Uint16 getPeerPort() const; 724 725 /** Returns DIMSE timeout in seconds for receiving data. If the blocking 726 * mode is DIMSE_NONBLOCKING the SCU will try to read data from 727 * the incoming socket stream for the number of seconds configured. 728 * @return The DIMSE timeout (in seconds) configured 729 */ 730 Uint32 getDIMSETimeout() const; 731 732 /** Returns ACSE timeout in seconds used by timer for message timeouts during 733 * association negotiation. 734 * @return The ACSE timeout (in seconds) configured 735 */ 736 Uint32 getACSETimeout() const; 737 738 /** Returns the timeout configured defining how long SCU will wait for the 739 * SCP when requesting an association. -1 means infinite waiting (blocking), 740 * 0 means no waiting at all. Note that this is currently a global DCMTK setting. 741 * @return The connection timeout (in seconds) 742 */ 743 Sint32 getConnectionTimeout() const; 744 745 /** Returns the storage directory used for storing objects received with C-STORE requests 746 * in the context of C-GET sessions. Default is empty string which refers to the current 747 * working directory. 748 * @return The storage directory 749 */ 750 OFString getStorageDir() const; 751 752 /** Returns the storage mode enabled 753 * @return The storage mode enabled 754 */ 755 DcmStorageMode getStorageMode() const; 756 757 /** Returns the verbose presentation context mode configured specifying whether details 758 * on the presentation contexts (negotiated during association setup) should be shown in 759 * verbose or debug mode. The latter is the default. 760 * @return The current verbose presentation context mode. Show details on the 761 * presentation contexts on INFO log level (verbose) if OFTrue and on DEBUG 762 * level if OFFalse. 763 */ 764 OFBool getVerbosePCMode() const; 765 766 /** Returns the mode that specifies whether the transfer syntax of the dataset can be 767 * changed for network transmission. This mainly covers the compression/decompression 768 * of datasets, which is disabled by default. 769 * @return The current dataset conversion mode, enabled if OFTrue 770 */ 771 OFBool getDatasetConversionMode() const; 772 773 /** Returns the mode that specifies whether the progress of sending and receiving DIMSE 774 * messages is notified by calling notifySENDProgress() and notifyRECEIVEProgress(), 775 * respectively. The progress notification is enabled by default. 776 * @return The current progress notification mode, enabled if OFTrue 777 */ 778 OFBool getProgressNotificationMode() const; 779 780 /** Returns whether SCU is configured to create a TLS connection with the SCP 781 * @return OFFalse for this class but may be overridden by derived classes 782 */ 783 OFBool getTLSEnabled() const; 784 785 /** Deletes internal networking structures from memory */ 786 void freeNetwork(); 787 788 protected: 789 /** Sends a DIMSE command and possibly also a dataset from a data object via network to 790 * another DICOM application 791 * @param presID [in] Presentation context ID to be used for message 792 * @param msg [in] Structure that represents a certain DIMSE command which 793 * shall be sent 794 * @param dataObject [in] The instance data which shall be sent to the other DICOM 795 * application; NULL, if there is none 796 * @param commandSet [out] If this parameter is not NULL it will return a copy of the 797 * DIMSE command which is sent to the other DICOM application 798 * @return EC_Normal if sending request was successful, an error code otherwise 799 */ 800 OFCondition sendDIMSEMessage(const T_ASC_PresentationContextID presID, 801 T_DIMSE_Message* msg, 802 DcmDataset* dataObject, 803 DcmDataset** commandSet = NULL); 804 805 /** Returns SOP Class UID, SOP Instance UID and original transfer syntax for a given dataset. 806 * If the dataset is NULL, all returned values will be undefined (i.e. empty or EXS_Unknown). 807 * @param dataset [in] The dataset to read from 808 * @param sopClassUID [out] The value of attribute SOP Class UID if present 809 * @param sopInstanceUID [out] The value of attribute SOP Instance UID if present 810 * @param transferSyntax [out] The value of transfer syntax that originally was read from 811 * disk. Will be unknown if the dataset was created in memory. 812 * @return EC_Normal if all information could be retrieved and is valid, an error code 813 * otherwise 814 */ 815 OFCondition getDatasetInfo(DcmDataset* dataset, 816 OFString& sopClassUID, 817 OFString& sopInstanceUID, 818 E_TransferSyntax& transferSyntax); 819 820 /** Tells DcmSCU to use a secure TLS connection described by the given TLS layer 821 * @param tlayer [in] The TLS transport layer including all TLS parameters 822 * @return EC_Normal if given transport layer is ok, an error code otherwise 823 */ 824 OFCondition useSecureConnection(DcmTransportLayer* tlayer); 825 826 /** Receive DIMSE command (excluding dataset!) over the currently open association 827 * @param presID [out] Contains in the end the ID of the presentation context 828 * which was specified in the DIMSE command received 829 * @param msg [out] The message received 830 * @param statusDetail [out] If a non-NULL value is passed this variable will in the end 831 * contain detailed information with regard to the status 832 * information which is captured in the status element 833 * (0000,0900). Note that the value for element (0000,0900) is 834 * not contained in this return value but in internal msg. For 835 * details on the structure of this object, see DICOM standard 836 * part 7, annex C). 837 * @param commandSet [out] If this parameter is not NULL, it will return a copy of the 838 * DIMSE command which was received from the other DICOM 839 * application. The caller is responsible to de-allocate the 840 * returned object! 841 * @param timeout [in] If this parameter is not 0, it specifies the timeout (in 842 * seconds) to be used for receiving the DIMSE command. 843 * Otherwise, the default timeout value is used (see 844 * setDIMSETimeout()). 845 * @return EC_Normal if command could be received successfully, an error code otherwise 846 */ 847 OFCondition receiveDIMSECommand(T_ASC_PresentationContextID* presID, 848 T_DIMSE_Message* msg, 849 DcmDataset** statusDetail, 850 DcmDataset** commandSet = NULL, 851 const Uint32 timeout = 0); 852 853 /** Receives one dataset (of instance data) via network from another DICOM application 854 * @param presID [out] Contains in the end the ID of the presentation context which 855 * was used in the PDVs that were received on the network. If the 856 * PDVs show different presentation context IDs, this function 857 * will return an error. 858 * @param dataObject [out] Contains in the end the information which was received over 859 * the network 860 * @return EC_Normal if dataset could be received successfully, an error code otherwise 861 */ 862 OFCondition receiveDIMSEDataset(T_ASC_PresentationContextID* presID, DcmDataset** dataObject); 863 864 /** clear list of presentation contexts. In addition, any currently selected association 865 * configuration file is disabled. 866 */ 867 void clearPresentationContexts(); 868 869 /** After negotiation association, this call returns the presentation context belonging 870 * to the given presentation context ID 871 * @param presID [in] The presentation context ID to look for 872 * @param abstractSyntax [out] The abstract syntax (UID) for that ID. 873 * Empty, if such a presentation context does not exist. 874 * @param transferSyntax [out] The transfer syntax (UID) for that ID. 875 * Empty, if such a presentation context does not exist. 876 */ 877 void findPresentationContext(const T_ASC_PresentationContextID presID, 878 OFString& abstractSyntax, 879 OFString& transferSyntax); 880 881 /* *********************************************************************** 882 * Functions particularly interesting for overwriting in derived classes 883 * *********************************************************************** */ 884 885 /** This function is called if an object was received due to a C-GET request and can be 886 * overwritten by a user in order to be informed about such an event. The default 887 * implementation just prints a DEBUG message. Note that this function is not called if 888 * the SCU is in storage mode DCMSCU_STORAGE_IGNORE. 889 * @param filename [in] The filename written 890 * @param sopClassUID [in] The SOP Class UID of the object written 891 * @param sopInstanceUID [in] The SOP Instance UID of the object written 892 */ 893 virtual void 894 notifyInstanceStored(const OFString& filename, const OFString& sopClassUID, const OFString& sopInstanceUID) const; 895 896 /** This function is called while sending DIMSE messages, i.e.\ on each PDV of a dataset. 897 * The default implementation just prints a TRACE message on the number of bytes sent so 898 * far. By overwriting this method, the progress of the send process can be shown to the 899 * user in a more appropriate way. The progress notification can also be disabled 900 * completely by calling setProgressNotificationMode(). 901 * @param byteCount [in] Number of bytes sent so far 902 */ 903 virtual void notifySENDProgress(const unsigned long byteCount); 904 905 /** This function is called while receiving DIMSE messages, i.e.\ on each PDV of a dataset. 906 * The default implementation just prints a TRACE message on the number of bytes received 907 * so far. By overwriting this method, the progress of the receive process can be shown to 908 * the user in a more appropriate way. The progress notification can also be disabled 909 * completely by calling setProgressNotificationMode(). 910 * @param byteCount [in] Number of bytes received so far 911 */ 912 virtual void notifyRECEIVEProgress(const unsigned long byteCount); 913 914 /** Check given N-EVENT-REPORT request and dataset for validity. This method is called by 915 * handleEVENTREPORTRequest() before sending the response in order to determine the 916 * DIMSE status code to be used for the response message. 917 * @param request [in] The N-EVENT-REPORT request message data structure 918 * @param reqDataset [in] The N-EVENT-REPORT request dataset received. Might be NULL. 919 * @return DIMSE status code to be used for the N-EVENT-REPORT response. 920 * Always returns STATUS_Success (0). Derived classes should, therefore, 921 * overwrite this method and return a more appropriate value based on the 922 * result of the checks performed. 923 */ 924 virtual Uint16 checkEVENTREPORTRequest(T_DIMSE_N_EventReportRQ& request, DcmDataset* reqDataset); 925 926 /** Sends back a C-STORE response on the given presentation context, with the designated 927 * status, fitting the corresponding C-STORE request. 928 * @param presID [in] The presentation context ID to be used. 929 * @param status [in] The storage DIMSE status to be used. 930 * @param request [in] The C-STORE request that should be responded to. 931 * @result EC_Normal if the response could be sent, error otherwise. 932 */ 933 virtual OFCondition 934 sendSTOREResponse(T_ASC_PresentationContextID presID, Uint16 status, const T_DIMSE_C_StoreRQ& request); 935 936 /** Helper function that generates a storage filename by extracting SOP Class and SOP 937 * Instance UID from a dataset and combining that with the configured storage directory. 938 * The SOP class is used to create an initial two letter abbreviation for the 939 * corresponding modality, e.g. CT. An example for a storage filename created by this 940 * function is "/storage_dir/CT.1.2.3.4.5" for a CT with SOP Instance UID "1.2.3.4.5". 941 * This function might be overwritten to change the filename behaviour completely. This 942 * function is only called if the SCU is in DCMSCU_STORAGE_DISK mode. 943 * @param dataset [in] The dataset that should be stored to disk 944 * @result Non-empty string if successful, otherwise empty string. 945 */ 946 virtual OFString createStorageFilename(DcmDataset* dataset); 947 948 /** Receives a DICOM dataset on a given presentation context ID but does not store it in 949 * memory or disk, thus ignoring it. 950 * @param presID [in] The presentation context to be used 951 * @param request [in] The corresponding C-STORE request 952 * @return EC_Normal if ignoring worked, error code otherwise. 953 */ 954 virtual OFCondition ignoreSTORERequest(T_ASC_PresentationContextID presID, const T_DIMSE_C_StoreRQ& request); 955 956 /* Callback functions (static) */ 957 958 /** Callback function used for sending DIMSE messages. 959 * @param callbackContext [in] The desired user callback data 960 * @param byteCount [in] Progress bytes count 961 */ 962 static void callbackSENDProgress(void* callbackContext, unsigned long byteCount); 963 964 /** Callback function used for receiving DIMSE messages. 965 * @param callbackContext [in] The desired user callback data 966 * @param byteCount [in] Progress bytes count 967 */ 968 static void callbackRECEIVEProgress(void* callbackContext, unsigned long byteCount); 969 970 private: 971 /** Private undefined copy-constructor. Shall never be called. 972 * @param src Source object 973 */ 974 DcmSCU(const DcmSCU& src); 975 976 /** Private undefined operator=. Shall never be called. 977 * @param src Source object 978 * @return Reference to this 979 */ 980 DcmSCU& operator=(const DcmSCU& src); 981 982 /// Association of this SCU. This class only handles 1 association at a time. 983 T_ASC_Association* m_assoc; 984 985 /// The DICOM network the association is based on 986 T_ASC_Network* m_net; 987 988 /// Association parameters 989 T_ASC_Parameters* m_params; 990 991 /// Configuration file for presentation contexts (optional) 992 OFString m_assocConfigFilename; 993 994 /// Profile in configuration file that should be used (optional) 995 OFString m_assocConfigProfile; 996 997 /// Defines presentation context, consisting of one abstract syntax name 998 /// and a list of transfer syntaxes for this abstract syntax 999 struct DCMTK_DCMNET_EXPORT DcmSCUPresContext 1000 { 1001 /** Default constructor 1002 */ DcmSCUPresContextDcmSCUPresContext1003 DcmSCUPresContext() 1004 : abstractSyntaxName() 1005 , transferSyntaxes() 1006 , roleSelect(ASC_SC_ROLE_DEFAULT) 1007 { 1008 } 1009 /// Abstract Syntax Name of Presentation Context 1010 OFString abstractSyntaxName; 1011 /// List of Transfer Syntaxes for Presentation Context 1012 OFList<OFString> transferSyntaxes; 1013 /// Role Selection 1014 T_ASC_SC_ROLE roleSelect; 1015 }; 1016 1017 /// List of presentation contexts that should be negotiated 1018 OFList<DcmSCUPresContext> m_presContexts; 1019 1020 /// Configuration file containing association parameters 1021 OFString m_assocConfigFile; 1022 1023 /// The last DIMSE successfully sent, unresponded DIMSE request 1024 T_DIMSE_Message* m_openDIMSERequest; 1025 1026 /// Maximum PDU size (default: 16384 bytes) 1027 Uint32 m_maxReceivePDULength; 1028 1029 /// DIMSE blocking mode (default: blocking) 1030 T_DIMSE_BlockingMode m_blockMode; 1031 1032 /// AE title of this application (default: ANY-SCU) 1033 OFString m_ourAETitle; 1034 1035 /// Peer host (IP or host name) 1036 OFString m_peer; 1037 1038 /// AE title of remote application (default: ANY-SCP) 1039 OFString m_peerAETitle; 1040 1041 /// Port of remote application entity (default: 104) 1042 Uint16 m_peerPort; 1043 1044 /// DIMSE timeout (default: unlimited) 1045 Uint32 m_dimseTimeout; 1046 1047 /// ACSE timeout (default: 30 seconds) 1048 Uint32 m_acseTimeout; 1049 1050 /// Storage directory for objects received with C-STORE due to a running 1051 /// C-GET session. By default, the received objects are stored in the current 1052 /// working directory. 1053 OFString m_storageDir; 1054 1055 /// Set whether bit preserving storage should be enabled, i.e.\ any objects 1056 /// retrieved via C-STORE should be written directly to disk without any data 1057 /// correction/re-computation (e.g.\ group length calculations, padding, etc.). 1058 /// This is especially interesting for retaining valid signatures, and also to 1059 /// receive huge files which cannot be fully received in memory. 1060 /// Default value: DCMSCU_STORAGE_DISK 1061 DcmStorageMode m_storageMode; 1062 1063 /// Verbose PC mode (default: disabled) 1064 OFBool m_verbosePCMode; 1065 1066 /// Dataset conversion mode (default: disabled) 1067 OFBool m_datasetConversionMode; 1068 1069 /// Progress notification mode (default: enabled) 1070 OFBool m_progressNotificationMode; 1071 1072 /** Returns next available message ID free to be used by SCU 1073 * @return Next free message ID 1074 */ 1075 Uint16 nextMessageID(); 1076 }; 1077 1078 #endif // SCU_H 1079