Mercurial > hg > orthanc
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 } |