comparison UnitTestsSources/FromDcmtkTests.cpp @ 3821:f2488b645f5f transcoding

adding storage commitment to DicomAssociation
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 09 Apr 2020 18:29:42 +0200
parents f89eac983c9b
children 0d5f3a438e14
comparison
equal deleted inserted replaced
3820:f89eac983c9b 3821:f2488b645f5f
2450 std::string localAet_; 2450 std::string localAet_;
2451 std::string remoteAet_; 2451 std::string remoteAet_;
2452 std::string remoteHost_; 2452 std::string remoteHost_;
2453 uint16_t remotePort_; 2453 uint16_t remotePort_;
2454 ModalityManufacturer manufacturer_; 2454 ModalityManufacturer manufacturer_;
2455 DicomAssociationRole role_;
2456 uint32_t timeout_; 2455 uint32_t timeout_;
2457 2456
2458 void ReadDefaultTimeout() 2457 void ReadDefaultTimeout()
2459 { 2458 {
2460 boost::mutex::scoped_lock lock(defaultTimeoutMutex_); 2459 boost::mutex::scoped_lock lock(defaultTimeoutMutex_);
2465 DicomAssociationParameters() : 2464 DicomAssociationParameters() :
2466 localAet_("STORESCU"), 2465 localAet_("STORESCU"),
2467 remoteAet_("ANY-SCP"), 2466 remoteAet_("ANY-SCP"),
2468 remoteHost_("127.0.0.1"), 2467 remoteHost_("127.0.0.1"),
2469 remotePort_(104), 2468 remotePort_(104),
2470 manufacturer_(ModalityManufacturer_Generic), 2469 manufacturer_(ModalityManufacturer_Generic)
2471 role_(DicomAssociationRole_Default)
2472 { 2470 {
2473 ReadDefaultTimeout(); 2471 ReadDefaultTimeout();
2474 } 2472 }
2475 2473
2476 DicomAssociationParameters(const std::string& localAet, 2474 DicomAssociationParameters(const std::string& localAet,
2478 localAet_(localAet), 2476 localAet_(localAet),
2479 remoteAet_(remote.GetApplicationEntityTitle()), 2477 remoteAet_(remote.GetApplicationEntityTitle()),
2480 remoteHost_(remote.GetHost()), 2478 remoteHost_(remote.GetHost()),
2481 remotePort_(remote.GetPortNumber()), 2479 remotePort_(remote.GetPortNumber()),
2482 manufacturer_(remote.GetManufacturer()), 2480 manufacturer_(remote.GetManufacturer()),
2483 role_(DicomAssociationRole_Default),
2484 timeout_(defaultTimeout_) 2481 timeout_(defaultTimeout_)
2485 { 2482 {
2486 ReadDefaultTimeout(); 2483 ReadDefaultTimeout();
2487 } 2484 }
2488 2485
2509 ModalityManufacturer GetRemoteManufacturer() const 2506 ModalityManufacturer GetRemoteManufacturer() const
2510 { 2507 {
2511 return manufacturer_; 2508 return manufacturer_;
2512 } 2509 }
2513 2510
2514 DicomAssociationRole GetRole() const
2515 {
2516 return role_;
2517 }
2518
2519 void SetLocalApplicationEntityTitle(const std::string& aet) 2511 void SetLocalApplicationEntityTitle(const std::string& aet)
2520 { 2512 {
2521 localAet_ = aet; 2513 localAet_ = aet;
2522 } 2514 }
2523 2515
2543 } 2535 }
2544 2536
2545 void SetRemoteManufacturer(ModalityManufacturer manufacturer) 2537 void SetRemoteManufacturer(ModalityManufacturer manufacturer)
2546 { 2538 {
2547 manufacturer_ = manufacturer; 2539 manufacturer_ = manufacturer;
2548 }
2549
2550 void SetRole(DicomAssociationRole role)
2551 {
2552 role_ = role;
2553 } 2540 }
2554 2541
2555 void SetRemoteModality(const RemoteModalityParameters& parameters) 2542 void SetRemoteModality(const RemoteModalityParameters& parameters)
2556 { 2543 {
2557 SetRemoteApplicationEntityTitle(parameters.GetApplicationEntityTitle()); 2544 SetRemoteApplicationEntityTitle(parameters.GetApplicationEntityTitle());
2564 { 2551 {
2565 return (localAet_ == other.localAet_ && 2552 return (localAet_ == other.localAet_ &&
2566 remoteAet_ == other.remoteAet_ && 2553 remoteAet_ == other.remoteAet_ &&
2567 remoteHost_ == other.remoteHost_ && 2554 remoteHost_ == other.remoteHost_ &&
2568 remotePort_ == other.remotePort_ && 2555 remotePort_ == other.remotePort_ &&
2569 manufacturer_ == other.manufacturer_ && 2556 manufacturer_ == other.manufacturer_);
2570 role_ == other.role_);
2571 } 2557 }
2572 2558
2573 void SetTimeout(uint32_t seconds) 2559 void SetTimeout(uint32_t seconds)
2574 { 2560 {
2575 timeout_ = seconds; 2561 timeout_ = seconds;
2644 } 2630 }
2645 } 2631 }
2646 }; 2632 };
2647 2633
2648 2634
2635 static void FillSopSequence(DcmDataset& dataset,
2636 const DcmTagKey& tag,
2637 const std::vector<std::string>& sopClassUids,
2638 const std::vector<std::string>& sopInstanceUids,
2639 const std::vector<StorageCommitmentFailureReason>& failureReasons,
2640 bool hasFailureReasons)
2641 {
2642 assert(sopClassUids.size() == sopInstanceUids.size() &&
2643 (hasFailureReasons ?
2644 failureReasons.size() == sopClassUids.size() :
2645 failureReasons.empty()));
2646
2647 if (sopInstanceUids.empty())
2648 {
2649 // Add an empty sequence
2650 if (!dataset.insertEmptyElement(tag).good())
2651 {
2652 throw OrthancException(ErrorCode_InternalError);
2653 }
2654 }
2655 else
2656 {
2657 for (size_t i = 0; i < sopClassUids.size(); i++)
2658 {
2659 std::unique_ptr<DcmItem> item(new DcmItem);
2660 if (!item->putAndInsertString(DCM_ReferencedSOPClassUID, sopClassUids[i].c_str()).good() ||
2661 !item->putAndInsertString(DCM_ReferencedSOPInstanceUID, sopInstanceUids[i].c_str()).good() ||
2662 (hasFailureReasons &&
2663 !item->putAndInsertUint16(DCM_FailureReason, failureReasons[i]).good()) ||
2664 !dataset.insertSequenceItem(tag, item.release()).good())
2665 {
2666 throw OrthancException(ErrorCode_InternalError);
2667 }
2668 }
2669 }
2670 }
2671
2672
2649 class DicomAssociation : public boost::noncopyable 2673 class DicomAssociation : public boost::noncopyable
2650 { 2674 {
2651 private: 2675 private:
2652 // This is the maximum number of presentation context IDs (the 2676 // This is the maximum number of presentation context IDs (the
2653 // number of odd integers between 1 and 255) 2677 // number of odd integers between 1 and 255)
2660 std::set<DicomTransferSyntax> transferSyntaxes_; 2684 std::set<DicomTransferSyntax> transferSyntaxes_;
2661 }; 2685 };
2662 2686
2663 typedef std::map<std::string, std::map<DicomTransferSyntax, uint8_t> > AcceptedPresentationContexts; 2687 typedef std::map<std::string, std::map<DicomTransferSyntax, uint8_t> > AcceptedPresentationContexts;
2664 2688
2689 DicomAssociationRole role_;
2665 bool isOpen_; 2690 bool isOpen_;
2666 std::vector<ProposedPresentationContext> proposed_; 2691 std::vector<ProposedPresentationContext> proposed_;
2667 AcceptedPresentationContexts accepted_; 2692 AcceptedPresentationContexts accepted_;
2668 T_ASC_Network* net_; 2693 T_ASC_Network* net_;
2669 T_ASC_Parameters* params_; 2694 T_ASC_Parameters* params_;
2670 T_ASC_Association* assoc_; 2695 T_ASC_Association* assoc_;
2671 2696
2672 void Initialize() 2697 void Initialize()
2673 { 2698 {
2699 role_ = DicomAssociationRole_Default;
2674 isOpen_ = false; 2700 isOpen_ = false;
2675 net_ = NULL; 2701 net_ = NULL;
2676 params_ = NULL; 2702 params_ = NULL;
2677 assoc_ = NULL; 2703 assoc_ = NULL;
2678 2704
2771 bool IsOpen() const 2797 bool IsOpen() const
2772 { 2798 {
2773 return isOpen_; 2799 return isOpen_;
2774 } 2800 }
2775 2801
2802 void SetRole(DicomAssociationRole role)
2803 {
2804 if (role_ != role)
2805 {
2806 Close();
2807 role_ = role;
2808 }
2809 }
2810
2776 void ClearPresentationContexts() 2811 void ClearPresentationContexts()
2777 { 2812 {
2778 Close(); 2813 Close();
2779 proposed_.clear(); 2814 proposed_.clear();
2780 proposed_.reserve(MAX_PROPOSED_PRESENTATIONS); 2815 proposed_.reserve(MAX_PROPOSED_PRESENTATIONS);
2803 { 2838 {
2804 dcmConnectionTimeout.set(acseTimeout); 2839 dcmConnectionTimeout.set(acseTimeout);
2805 } 2840 }
2806 2841
2807 T_ASC_SC_ROLE dcmtkRole; 2842 T_ASC_SC_ROLE dcmtkRole;
2808 switch (parameters.GetRole()) 2843 switch (role_)
2809 { 2844 {
2810 case DicomAssociationRole_Default: 2845 case DicomAssociationRole_Default:
2811 dcmtkRole = ASC_SC_ROLE_DEFAULT; 2846 dcmtkRole = ASC_SC_ROLE_DEFAULT;
2812 break; 2847 break;
2813 2848
3030 { 3065 {
3031 throw OrthancException(ErrorCode_BadSequenceOfCalls, 3066 throw OrthancException(ErrorCode_BadSequenceOfCalls,
3032 "The connection is not open"); 3067 "The connection is not open");
3033 } 3068 }
3034 } 3069 }
3070
3071
3072 static void ReportStorageCommitment(const DicomAssociationParameters& parameters,
3073 const std::string& transactionUid,
3074 const std::vector<std::string>& sopClassUids,
3075 const std::vector<std::string>& sopInstanceUids,
3076 const std::vector<StorageCommitmentFailureReason>& failureReasons)
3077 {
3078 if (sopClassUids.size() != sopInstanceUids.size() ||
3079 sopClassUids.size() != failureReasons.size())
3080 {
3081 throw OrthancException(ErrorCode_ParameterOutOfRange);
3082 }
3083
3084
3085 std::vector<std::string> successSopClassUids, successSopInstanceUids, failedSopClassUids, failedSopInstanceUids;
3086 std::vector<StorageCommitmentFailureReason> failedReasons;
3087
3088 successSopClassUids.reserve(sopClassUids.size());
3089 successSopInstanceUids.reserve(sopClassUids.size());
3090 failedSopClassUids.reserve(sopClassUids.size());
3091 failedSopInstanceUids.reserve(sopClassUids.size());
3092 failedReasons.reserve(sopClassUids.size());
3093
3094 for (size_t i = 0; i < sopClassUids.size(); i++)
3095 {
3096 switch (failureReasons[i])
3097 {
3098 case StorageCommitmentFailureReason_Success:
3099 successSopClassUids.push_back(sopClassUids[i]);
3100 successSopInstanceUids.push_back(sopInstanceUids[i]);
3101 break;
3102
3103 case StorageCommitmentFailureReason_ProcessingFailure:
3104 case StorageCommitmentFailureReason_NoSuchObjectInstance:
3105 case StorageCommitmentFailureReason_ResourceLimitation:
3106 case StorageCommitmentFailureReason_ReferencedSOPClassNotSupported:
3107 case StorageCommitmentFailureReason_ClassInstanceConflict:
3108 case StorageCommitmentFailureReason_DuplicateTransactionUID:
3109 failedSopClassUids.push_back(sopClassUids[i]);
3110 failedSopInstanceUids.push_back(sopInstanceUids[i]);
3111 failedReasons.push_back(failureReasons[i]);
3112 break;
3113
3114 default:
3115 {
3116 char buf[16];
3117 sprintf(buf, "%04xH", failureReasons[i]);
3118 throw OrthancException(ErrorCode_ParameterOutOfRange,
3119 "Unsupported failure reason for storage commitment: " + std::string(buf));
3120 }
3121 }
3122 }
3123
3124 DicomAssociation association;
3125
3126 {
3127 std::set<DicomTransferSyntax> transferSyntaxes;
3128 transferSyntaxes.insert(DicomTransferSyntax_LittleEndianExplicit);
3129 transferSyntaxes.insert(DicomTransferSyntax_LittleEndianImplicit);
3130
3131 association.SetRole(DicomAssociationRole_Scp);
3132 association.ProposePresentationContext(UID_StorageCommitmentPushModelSOPClass,
3133 transferSyntaxes);
3134 }
3135
3136 association.Open(parameters);
3137
3138 /**
3139 * N-EVENT-REPORT
3140 * http://dicom.nema.org/medical/dicom/2019a/output/chtml/part04/sect_J.3.3.html
3141 * http://dicom.nema.org/medical/dicom/2019a/output/chtml/part07/chapter_10.html#table_10.1-1
3142 *
3143 * Status code:
3144 * http://dicom.nema.org/medical/dicom/2019a/output/chtml/part07/chapter_10.html#sect_10.1.1.1.8
3145 **/
3146
3147 /**
3148 * Send the "EVENT_REPORT_RQ" request
3149 **/
3150
3151 LOG(INFO) << "Reporting modality \""
3152 << parameters.GetRemoteApplicationEntityTitle()
3153 << "\" about storage commitment transaction: " << transactionUid
3154 << " (" << successSopClassUids.size() << " successes, "
3155 << failedSopClassUids.size() << " failures)";
3156 const DIC_US messageId = association.GetDcmtkAssociation().nextMsgID++;
3157
3158 {
3159 T_DIMSE_Message message;
3160 memset(&message, 0, sizeof(message));
3161 message.CommandField = DIMSE_N_EVENT_REPORT_RQ;
3162
3163 T_DIMSE_N_EventReportRQ& content = message.msg.NEventReportRQ;
3164 content.MessageID = messageId;
3165 strncpy(content.AffectedSOPClassUID, UID_StorageCommitmentPushModelSOPClass, DIC_UI_LEN);
3166 strncpy(content.AffectedSOPInstanceUID, UID_StorageCommitmentPushModelSOPInstance, DIC_UI_LEN);
3167 content.DataSetType = DIMSE_DATASET_PRESENT;
3168
3169 DcmDataset dataset;
3170 if (!dataset.putAndInsertString(DCM_TransactionUID, transactionUid.c_str()).good())
3171 {
3172 throw OrthancException(ErrorCode_InternalError);
3173 }
3174
3175 {
3176 std::vector<StorageCommitmentFailureReason> empty;
3177 FillSopSequence(dataset, DCM_ReferencedSOPSequence, successSopClassUids,
3178 successSopInstanceUids, empty, false);
3179 }
3180
3181 // http://dicom.nema.org/medical/dicom/2019a/output/chtml/part04/sect_J.3.3.html
3182 if (failedSopClassUids.empty())
3183 {
3184 content.EventTypeID = 1; // "Storage Commitment Request Successful"
3185 }
3186 else
3187 {
3188 content.EventTypeID = 2; // "Storage Commitment Request Complete - Failures Exist"
3189
3190 // Failure reason
3191 // http://dicom.nema.org/medical/dicom/2019a/output/chtml/part03/sect_C.14.html#sect_C.14.1.1
3192 FillSopSequence(dataset, DCM_FailedSOPSequence, failedSopClassUids,
3193 failedSopInstanceUids, failedReasons, true);
3194 }
3195
3196 int presID = ASC_findAcceptedPresentationContextID(
3197 &association.GetDcmtkAssociation(), UID_StorageCommitmentPushModelSOPClass);
3198 if (presID == 0)
3199 {
3200 throw OrthancException(ErrorCode_NetworkProtocol, "Storage commitment - "
3201 "Unable to send N-EVENT-REPORT request to AET: " +
3202 parameters.GetRemoteApplicationEntityTitle());
3203 }
3204
3205 if (!DIMSE_sendMessageUsingMemoryData(
3206 &association.GetDcmtkAssociation(), presID, &message, NULL /* status detail */,
3207 &dataset, NULL /* callback */, NULL /* callback context */,
3208 NULL /* commandSet */).good())
3209 {
3210 throw OrthancException(ErrorCode_NetworkProtocol);
3211 }
3212 }
3213
3214 /**
3215 * Read the "EVENT_REPORT_RSP" response
3216 **/
3217
3218 {
3219 T_ASC_PresentationContextID presID = 0;
3220 T_DIMSE_Message message;
3221
3222 if (!DIMSE_receiveCommand(&association.GetDcmtkAssociation(),
3223 (parameters.HasTimeout() ? DIMSE_NONBLOCKING : DIMSE_BLOCKING),
3224 parameters.GetTimeout(), &presID, &message,
3225 NULL /* no statusDetail */).good() ||
3226 message.CommandField != DIMSE_N_EVENT_REPORT_RSP)
3227 {
3228 throw OrthancException(ErrorCode_NetworkProtocol, "Storage commitment - "
3229 "Unable to read N-EVENT-REPORT response from AET: " +
3230 parameters.GetRemoteApplicationEntityTitle());
3231 }
3232
3233 const T_DIMSE_N_EventReportRSP& content = message.msg.NEventReportRSP;
3234 if (content.MessageIDBeingRespondedTo != messageId ||
3235 !(content.opts & O_NEVENTREPORT_AFFECTEDSOPCLASSUID) ||
3236 !(content.opts & O_NEVENTREPORT_AFFECTEDSOPINSTANCEUID) ||
3237 //(content.opts & O_NEVENTREPORT_EVENTTYPEID) || // Pedantic test - The "content.EventTypeID" is not used by Orthanc
3238 std::string(content.AffectedSOPClassUID) != UID_StorageCommitmentPushModelSOPClass ||
3239 std::string(content.AffectedSOPInstanceUID) != UID_StorageCommitmentPushModelSOPInstance ||
3240 content.DataSetType != DIMSE_DATASET_NULL)
3241 {
3242 throw OrthancException(ErrorCode_NetworkProtocol, "Storage commitment - "
3243 "Badly formatted N-EVENT-REPORT response from AET: " +
3244 parameters.GetRemoteApplicationEntityTitle());
3245 }
3246
3247 if (content.DimseStatus != 0 /* success */)
3248 {
3249 throw OrthancException(ErrorCode_NetworkProtocol, "Storage commitment - "
3250 "The request cannot be handled by remote AET: " +
3251 parameters.GetRemoteApplicationEntityTitle());
3252 }
3253 }
3254
3255 association.Close();
3256 }
3257
3258 static void RequestStorageCommitment(const DicomAssociationParameters& parameters,
3259 const std::string& transactionUid,
3260 const std::vector<std::string>& sopClassUids,
3261 const std::vector<std::string>& sopInstanceUids)
3262 {
3263 if (sopClassUids.size() != sopInstanceUids.size())
3264 {
3265 throw OrthancException(ErrorCode_ParameterOutOfRange);
3266 }
3267
3268 for (size_t i = 0; i < sopClassUids.size(); i++)
3269 {
3270 if (sopClassUids[i].empty() ||
3271 sopInstanceUids[i].empty())
3272 {
3273 throw OrthancException(ErrorCode_ParameterOutOfRange,
3274 "The SOP class/instance UIDs cannot be empty, found: \"" +
3275 sopClassUids[i] + "\" / \"" + sopInstanceUids[i] + "\"");
3276 }
3277 }
3278
3279 if (transactionUid.size() < 5 ||
3280 transactionUid.substr(0, 5) != "2.25.")
3281 {
3282 throw OrthancException(ErrorCode_ParameterOutOfRange);
3283 }
3284
3285 DicomAssociation association;
3286
3287 {
3288 std::set<DicomTransferSyntax> transferSyntaxes;
3289 transferSyntaxes.insert(DicomTransferSyntax_LittleEndianExplicit);
3290 transferSyntaxes.insert(DicomTransferSyntax_LittleEndianImplicit);
3291
3292 association.SetRole(DicomAssociationRole_Default);
3293 association.ProposePresentationContext(UID_StorageCommitmentPushModelSOPClass,
3294 transferSyntaxes);
3295 }
3296
3297 association.Open(parameters);
3298
3299 /**
3300 * N-ACTION
3301 * http://dicom.nema.org/medical/dicom/2019a/output/chtml/part04/sect_J.3.2.html
3302 * http://dicom.nema.org/medical/dicom/2019a/output/chtml/part07/chapter_10.html#table_10.1-4
3303 *
3304 * Status code:
3305 * http://dicom.nema.org/medical/dicom/2019a/output/chtml/part07/chapter_10.html#sect_10.1.1.1.8
3306 **/
3307
3308 /**
3309 * Send the "N_ACTION_RQ" request
3310 **/
3311
3312 LOG(INFO) << "Request to modality \""
3313 << parameters.GetRemoteApplicationEntityTitle()
3314 << "\" about storage commitment for " << sopClassUids.size()
3315 << " instances, with transaction UID: " << transactionUid;
3316 const DIC_US messageId = association.GetDcmtkAssociation().nextMsgID++;
3317
3318 {
3319 T_DIMSE_Message message;
3320 memset(&message, 0, sizeof(message));
3321 message.CommandField = DIMSE_N_ACTION_RQ;
3322
3323 T_DIMSE_N_ActionRQ& content = message.msg.NActionRQ;
3324 content.MessageID = messageId;
3325 strncpy(content.RequestedSOPClassUID, UID_StorageCommitmentPushModelSOPClass, DIC_UI_LEN);
3326 strncpy(content.RequestedSOPInstanceUID, UID_StorageCommitmentPushModelSOPInstance, DIC_UI_LEN);
3327 content.ActionTypeID = 1; // "Request Storage Commitment"
3328 content.DataSetType = DIMSE_DATASET_PRESENT;
3329
3330 DcmDataset dataset;
3331 if (!dataset.putAndInsertString(DCM_TransactionUID, transactionUid.c_str()).good())
3332 {
3333 throw OrthancException(ErrorCode_InternalError);
3334 }
3335
3336 {
3337 std::vector<StorageCommitmentFailureReason> empty;
3338 FillSopSequence(dataset, DCM_ReferencedSOPSequence, sopClassUids, sopInstanceUids, empty, false);
3339 }
3340
3341 int presID = ASC_findAcceptedPresentationContextID(
3342 &association.GetDcmtkAssociation(), UID_StorageCommitmentPushModelSOPClass);
3343 if (presID == 0)
3344 {
3345 throw OrthancException(ErrorCode_NetworkProtocol, "Storage commitment - "
3346 "Unable to send N-ACTION request to AET: " +
3347 parameters.GetRemoteApplicationEntityTitle());
3348 }
3349
3350 if (!DIMSE_sendMessageUsingMemoryData(
3351 &association.GetDcmtkAssociation(), presID, &message, NULL /* status detail */,
3352 &dataset, NULL /* callback */, NULL /* callback context */,
3353 NULL /* commandSet */).good())
3354 {
3355 throw OrthancException(ErrorCode_NetworkProtocol);
3356 }
3357 }
3358
3359 /**
3360 * Read the "N_ACTION_RSP" response
3361 **/
3362
3363 {
3364 T_ASC_PresentationContextID presID = 0;
3365 T_DIMSE_Message message;
3366
3367 if (!DIMSE_receiveCommand(&association.GetDcmtkAssociation(),
3368 (parameters.HasTimeout() ? DIMSE_NONBLOCKING : DIMSE_BLOCKING),
3369 parameters.GetTimeout(), &presID, &message,
3370 NULL /* no statusDetail */).good() ||
3371 message.CommandField != DIMSE_N_ACTION_RSP)
3372 {
3373 throw OrthancException(ErrorCode_NetworkProtocol, "Storage commitment - "
3374 "Unable to read N-ACTION response from AET: " +
3375 parameters.GetRemoteApplicationEntityTitle());
3376 }
3377
3378 const T_DIMSE_N_ActionRSP& content = message.msg.NActionRSP;
3379 if (content.MessageIDBeingRespondedTo != messageId ||
3380 !(content.opts & O_NACTION_AFFECTEDSOPCLASSUID) ||
3381 !(content.opts & O_NACTION_AFFECTEDSOPINSTANCEUID) ||
3382 //(content.opts & O_NACTION_ACTIONTYPEID) || // Pedantic test - The "content.ActionTypeID" is not used by Orthanc
3383 std::string(content.AffectedSOPClassUID) != UID_StorageCommitmentPushModelSOPClass ||
3384 std::string(content.AffectedSOPInstanceUID) != UID_StorageCommitmentPushModelSOPInstance ||
3385 content.DataSetType != DIMSE_DATASET_NULL)
3386 {
3387 throw OrthancException(ErrorCode_NetworkProtocol, "Storage commitment - "
3388 "Badly formatted N-ACTION response from AET: " +
3389 parameters.GetRemoteApplicationEntityTitle());
3390 }
3391
3392 if (content.DimseStatus != 0 /* success */)
3393 {
3394 throw OrthancException(ErrorCode_NetworkProtocol, "Storage commitment - "
3395 "The request cannot be handled by remote AET: " +
3396 parameters.GetRemoteApplicationEntityTitle());
3397 }
3398 }
3399
3400 association.Close();
3401 }
3035 }; 3402 };
3036 3403
3037 3404
3038 3405
3039 static void TestAndCopyTag(DicomMap& result, 3406 static void TestAndCopyTag(DicomMap& result,
3666 const char* sopClass = UID_FINDModalityWorklistInformationModel; 4033 const char* sopClass = UID_FINDModalityWorklistInformationModel;
3667 4034
3668 FindInternal(result, dataset, sopClass, true, NULL); 4035 FindInternal(result, dataset, sopClass, true, NULL);
3669 } 4036 }
3670 }; 4037 };
3671
3672 } 4038 }
3673 4039
3674 4040
3675 TEST(Toto, DISABLED_DicomAssociation) 4041 TEST(Toto, DISABLED_DicomAssociation)
3676 { 4042 {