comparison OrthancServer/Sources/OrthancGetRequestHandler.cpp @ 4258:6f5d4bfb2c90

C-GET SCP: Fix responses and handling of cancel
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 26 Oct 2020 12:23:36 +0100
parents c046d559edb3
children 0ae2ca210077
comparison
equal deleted inserted replaced
4257:c046d559edb3 4258:6f5d4bfb2c90
64 { 64 {
65 // SBL - no logging to be done here. 65 // SBL - no logging to be done here.
66 } 66 }
67 } 67 }
68 68
69 OrthancGetRequestHandler::Status 69
70 OrthancGetRequestHandler::DoNext(T_ASC_Association* assoc) 70 bool OrthancGetRequestHandler::DoNext(T_ASC_Association* assoc)
71 { 71 {
72 if (position_ >= instances_.size()) 72 if (position_ >= instances_.size())
73 { 73 {
74 return Status_Failure; 74 throw OrthancException(ErrorCode_ParameterOutOfRange);
75 } 75 }
76 76
77 const std::string& id = instances_[position_++]; 77 const std::string& id = instances_[position_++];
78 78
79 std::string dicom; 79 std::string dicom;
80 context_.ReadDicom(dicom, id); 80 context_.ReadDicom(dicom, id);
81 81
82 if (dicom.empty()) 82 if (dicom.empty())
83 { 83 {
84 return Status_Failure; 84 throw OrthancException(ErrorCode_BadFileFormat);
85 } 85 }
86 86
87 std::unique_ptr<DcmFileFormat> parsed( 87 std::unique_ptr<DcmFileFormat> parsed(
88 FromDcmtkBridge::LoadFromMemoryBuffer(dicom.c_str(), dicom.size())); 88 FromDcmtkBridge::LoadFromMemoryBuffer(dicom.c_str(), dicom.size()));
89 89
105 } 105 }
106 106
107 std::string sopClassUid(a.c_str()); 107 std::string sopClassUid(a.c_str());
108 std::string sopInstanceUid(b.c_str()); 108 std::string sopInstanceUid(b.c_str());
109 109
110 Status status = PerformGetSubOp(assoc, sopClassUid, sopInstanceUid, parsed.release()); 110 return PerformGetSubOp(assoc, sopClassUid, sopInstanceUid, parsed.release());
111
112 if (getCancelled_)
113 {
114 LOG(INFO) << "C-GET SCP: Received C-Cancel RQ";
115 return Status_Failure;
116 }
117 else
118 {
119 return status;
120 }
121 } 111 }
122 112
123 113
124 void OrthancGetRequestHandler::AddFailedUIDInstance(const std::string& sopInstance) 114 void OrthancGetRequestHandler::AddFailedUIDInstance(const std::string& sopInstance)
125 { 115 {
229 // No preferred syntax was accepted 219 // No preferred syntax was accepted
230 return false; 220 return false;
231 } 221 }
232 222
233 223
234 OrthancGetRequestHandler::Status 224 bool OrthancGetRequestHandler::PerformGetSubOp(T_ASC_Association* assoc,
235 OrthancGetRequestHandler::PerformGetSubOp(T_ASC_Association* assoc, 225 const std::string& sopClassUid,
236 const std::string& sopClassUid, 226 const std::string& sopInstanceUid,
237 const std::string& sopInstanceUid, 227 DcmFileFormat* dicomRaw)
238 DcmFileFormat* dicomRaw)
239 { 228 {
240 assert(dicomRaw != NULL); 229 assert(dicomRaw != NULL);
241 std::unique_ptr<DcmFileFormat> dicom(dicomRaw); 230 std::unique_ptr<DcmFileFormat> dicom(dicomRaw);
242 231
243 DicomTransferSyntax sourceSyntax; 232 DicomTransferSyntax sourceSyntax;
244 if (!FromDcmtkBridge::LookupOrthancTransferSyntax(sourceSyntax, *dicom)) 233 if (!FromDcmtkBridge::LookupOrthancTransferSyntax(sourceSyntax, *dicom))
245 { 234 {
246 nFailed_++; 235 failedCount_++;
247 AddFailedUIDInstance(sopInstanceUid); 236 AddFailedUIDInstance(sopInstanceUid);
248 LOG(ERROR) << "C-GET SCP: Unknown transfer syntax: (" 237 throw OrthancException(ErrorCode_NetworkProtocol,
249 << dcmSOPClassUIDToModality(sopClassUid.c_str(), "OT") << ") " << sopClassUid; 238 "C-GET SCP: Unknown transfer syntax: (" +
250 return Status_Failure; 239 std::string(dcmSOPClassUIDToModality(sopClassUid.c_str(), "OT")) +
240 ") " + sopClassUid);
251 } 241 }
252 242
253 bool allowTranscoding = (context_.IsTranscodeDicomProtocol() && 243 bool allowTranscoding = (context_.IsTranscodeDicomProtocol() &&
254 remote_.IsTranscodingAllowed()); 244 remote_.IsTranscodingAllowed());
255 245
257 DicomTransferSyntax selectedSyntax; 247 DicomTransferSyntax selectedSyntax;
258 if (!SelectPresentationContext(presId, selectedSyntax, assoc, sopClassUid, 248 if (!SelectPresentationContext(presId, selectedSyntax, assoc, sopClassUid,
259 sourceSyntax, allowTranscoding) || 249 sourceSyntax, allowTranscoding) ||
260 presId == 0) 250 presId == 0)
261 { 251 {
262 nFailed_++; 252 failedCount_++;
263 AddFailedUIDInstance(sopInstanceUid); 253 AddFailedUIDInstance(sopInstanceUid);
264 LOG(ERROR) << "C-GET SCP: storeSCU: No presentation context for: (" 254 throw OrthancException(ErrorCode_NetworkProtocol,
265 << dcmSOPClassUIDToModality(sopClassUid.c_str(), "OT") << ") " << sopClassUid; 255 "C-GET SCP: storeSCU: No presentation context for: (" +
266 return Status_Failure; 256 std::string(dcmSOPClassUIDToModality(sopClassUid.c_str(), "OT")) +
257 ") " + sopClassUid);
267 } 258 }
268 else 259 else
269 { 260 {
270 LOG(INFO) << "C-GET SCP selected transfer syntax " << GetTransferSyntaxUid(selectedSyntax) 261 LOG(INFO) << "C-GET SCP selected transfer syntax " << GetTransferSyntaxUid(selectedSyntax)
271 << ", for source instance with SOP class " << sopClassUid 262 << ", for source instance with SOP class " << sopClassUid
279 if (pc.acceptedRole != ASC_SC_ROLE_DEFAULT && // "DEFAULT" is necessary for GinkgoCADx 270 if (pc.acceptedRole != ASC_SC_ROLE_DEFAULT && // "DEFAULT" is necessary for GinkgoCADx
280 pc.acceptedRole != ASC_SC_ROLE_SCP && 271 pc.acceptedRole != ASC_SC_ROLE_SCP &&
281 pc.acceptedRole != ASC_SC_ROLE_SCUSCP) 272 pc.acceptedRole != ASC_SC_ROLE_SCUSCP)
282 { 273 {
283 // the role is not appropriate 274 // the role is not appropriate
284 nFailed_++; 275 failedCount_++;
285 AddFailedUIDInstance(sopInstanceUid); 276 AddFailedUIDInstance(sopInstanceUid);
286 LOG(ERROR) << "C-GET SCP: storeSCU: [No presentation context with requestor SCP role for: (" 277 throw OrthancException(ErrorCode_NetworkProtocol,
287 << dcmSOPClassUIDToModality(sopClassUid.c_str(), "OT") << ") " << sopClassUid; 278 "C-GET SCP: storeSCU: [No presentation context with requestor SCP role for: (" +
288 return Status_Failure; 279 std::string(dcmSOPClassUIDToModality(sopClassUid.c_str(), "OT")) +
280 ") " + sopClassUid);
289 } 281 }
290 } 282 }
291 283
292 const DIC_US msgId = assoc->nextMsgID++; 284 const DIC_US msgId = assoc->nextMsgID++;
293 285
346 stDetail.reset(stDetailTmp); 338 stDetail.reset(stDetailTmp);
347 } 339 }
348 else 340 else
349 { 341 {
350 // Cannot transcode 342 // Cannot transcode
351 nFailed_++; 343 failedCount_++;
352 AddFailedUIDInstance(sopInstanceUid); 344 AddFailedUIDInstance(sopInstanceUid);
353 LOG(ERROR) << "C-GET SCP: Cannot transcode " << sopClassUid 345 throw OrthancException(ErrorCode_NotImplemented,
354 << " from transfer syntax " << GetTransferSyntaxUid(sourceSyntax) 346 "C-GET SCP: Cannot transcode " + sopClassUid +
355 << " to " << GetTransferSyntaxUid(selectedSyntax); 347 " from transfer syntax " + GetTransferSyntaxUid(sourceSyntax) +
356 return Status_Failure; 348 " to " + GetTransferSyntaxUid(selectedSyntax));
357 } 349 }
358 } 350 }
359 351
360 Status status; 352 bool isContinue;
353
361 if (cond.good()) 354 if (cond.good())
362 { 355 {
363 if (cancelParameters.cancelEncountered) 356 if (cancelParameters.cancelEncountered)
364 { 357 {
365 if (origPresId_ == cancelParameters.presId && 358 LOG(INFO) << "C-GET SCP: Received C-Cancel RQ";
366 origMsgId_ == cancelParameters.req.MessageIDBeingRespondedTo) 359 isContinue = false;
367 { 360 }
368 getCancelled_ = true; 361 else if (rsp.DimseStatus == STATUS_Success)
369 }
370 else
371 {
372 LOG(ERROR) << "C-GET SCP: Unexpected C-Cancel-RQ encountered: pid=" << (int)cancelParameters.presId
373 << ", mid=" << (int)cancelParameters.req.MessageIDBeingRespondedTo;
374 }
375 }
376
377 if (rsp.DimseStatus == STATUS_Success)
378 { 362 {
379 // everything ok 363 // everything ok
380 nCompleted_++; 364 completedCount_++;
381 status = Status_Success; 365 isContinue = true;
382 } 366 }
383 else if ((rsp.DimseStatus & 0xf000) == 0xb000) 367 else if ((rsp.DimseStatus & 0xf000) == 0xb000)
384 { 368 {
385 // a warning status message 369 // a warning status message
386 warningCount_++; 370 warningCount_++;
387 LOG(ERROR) << "C-GET SCP: Store Warning: Response Status: " 371 LOG(ERROR) << "C-GET SCP: Store Warning: Response Status: "
388 << DU_cstoreStatusString(rsp.DimseStatus); 372 << DU_cstoreStatusString(rsp.DimseStatus);
389 status = Status_Warning; 373 isContinue = true;
390 } 374 }
391 else 375 else
392 { 376 {
393 nFailed_++; 377 failedCount_++;
394 AddFailedUIDInstance(sopInstanceUid); 378 AddFailedUIDInstance(sopInstanceUid);
395 // print a status message 379 // print a status message
396 LOG(ERROR) << "C-GET SCP: Store Failed: Response Status: " 380 LOG(ERROR) << "C-GET SCP: Store Failed: Response Status: "
397 << DU_cstoreStatusString(rsp.DimseStatus); 381 << DU_cstoreStatusString(rsp.DimseStatus);
398 status = Status_Failure; 382 isContinue = true;
399 } 383 }
400 } 384 }
401 else 385 else
402 { 386 {
403 nFailed_++; 387 failedCount_++;
404 AddFailedUIDInstance(sopInstanceUid); 388 AddFailedUIDInstance(sopInstanceUid);
405 OFString temp_str; 389 OFString temp_str;
406 LOG(ERROR) << "C-GET SCP: storeSCU: Store Request Failed: " << DimseCondition::dump(temp_str, cond); 390 LOG(ERROR) << "C-GET SCP: storeSCU: Store Request Failed: " << DimseCondition::dump(temp_str, cond);
407 status = Status_Failure; 391 isContinue = true;
408 } 392 }
409 393
410 if (stDetail.get() != NULL) 394 if (stDetail.get() != NULL)
411 { 395 {
412 // It is impossible to directly use the "<<" stream construct 396 // It is impossible to directly use the "<<" stream construct
416 obj.dcmobj_.print(s); 400 obj.dcmobj_.print(s);
417 401
418 LOG(INFO) << " Status Detail: " << s.str(); 402 LOG(INFO) << " Status Detail: " << s.str();
419 } 403 }
420 404
421 return status; 405 return isContinue;
422 } 406 }
423 407
424 bool OrthancGetRequestHandler::LookupIdentifiers(std::list<std::string>& publicIds, 408 bool OrthancGetRequestHandler::LookupIdentifiers(std::list<std::string>& publicIds,
425 ResourceType level, 409 ResourceType level,
426 const DicomMap& input) const 410 const DicomMap& input) const
490 } 474 }
491 } 475 }
492 476
493 477
494 OrthancGetRequestHandler::OrthancGetRequestHandler(ServerContext& context) : 478 OrthancGetRequestHandler::OrthancGetRequestHandler(ServerContext& context) :
495 context_(context), 479 context_(context)
496 getCancelled_(false)
497 { 480 {
498 position_ = 0; 481 position_ = 0;
499 nRemaining_ = 0; 482 completedCount_ = 0;
500 nCompleted_ = 0;
501 warningCount_ = 0; 483 warningCount_ = 0;
502 nFailed_ = 0; 484 failedCount_ = 0;
503 timeout_ = 0; 485 timeout_ = 0;
504 } 486 }
505 487
506 488
507 bool OrthancGetRequestHandler::Handle(const DicomMap& input, 489 bool OrthancGetRequestHandler::Handle(const DicomMap& input,
573 instances_.push_back(*it); 555 instances_.push_back(*it);
574 } 556 }
575 } 557 }
576 558
577 failedUIDs_.clear(); 559 failedUIDs_.clear();
578 getCancelled_ = false; 560
579 561 completedCount_ = 0;
580 nRemaining_ = GetSubOperationCount(); 562 failedCount_ = 0;
581 nCompleted_ = 0;
582 nFailed_ = 0;
583 warningCount_ = 0; 563 warningCount_ = 0;
584 timeout_ = timeout; 564 timeout_ = timeout;
585 565
586 return true; 566 return true;
587 } 567 }