1 /** 2 * Orthanc - A Lightweight, RESTful DICOM Store 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics 4 * Department, University Hospital of Liege, Belgium 5 * Copyright (C) 2017-2021 Osimis S.A., Belgium 6 * 7 * This program is free software: you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public License 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this program. If not, see 19 * <http://www.gnu.org/licenses/>. 20 **/ 21 22 23 #include "../PrecompiledHeaders.h" 24 #include "DicomStoreUserConnection.h" 25 26 #include "../DicomParsing/FromDcmtkBridge.h" 27 #include "../DicomParsing/ParsedDicomFile.h" 28 #include "../Logging.h" 29 #include "../OrthancException.h" 30 #include "DicomAssociation.h" 31 32 #include <dcmtk/dcmdata/dcdeftag.h> 33 34 #include <list> 35 36 37 namespace Orthanc 38 { ProgressCallback(void *,T_DIMSE_StoreProgress * progress,T_DIMSE_C_StoreRQ * req)39 static void ProgressCallback(void * /*callbackData*/, 40 T_DIMSE_StoreProgress *progress, 41 T_DIMSE_C_StoreRQ * req) 42 { 43 if (req != NULL && 44 progress->state == DIMSE_StoreBegin) 45 { 46 OFString str; 47 CLOG(TRACE, DICOM) << "Sending Store Request:" << std::endl 48 << DIMSE_dumpMessage(str, *req, DIMSE_OUTGOING); 49 } 50 } 51 52 ProposeStorageClass(const std::string & sopClassUid,const std::set<DicomTransferSyntax> & sourceSyntaxes,bool hasPreferred,DicomTransferSyntax preferred)53 bool DicomStoreUserConnection::ProposeStorageClass(const std::string& sopClassUid, 54 const std::set<DicomTransferSyntax>& sourceSyntaxes, 55 bool hasPreferred, 56 DicomTransferSyntax preferred) 57 { 58 typedef std::list< std::set<DicomTransferSyntax> > GroupsOfSyntaxes; 59 60 GroupsOfSyntaxes groups; 61 62 // Firstly, add one group for each individual transfer syntax 63 for (std::set<DicomTransferSyntax>::const_iterator 64 it = sourceSyntaxes.begin(); it != sourceSyntaxes.end(); ++it) 65 { 66 std::set<DicomTransferSyntax> group; 67 group.insert(*it); 68 groups.push_back(group); 69 } 70 71 // Secondly, add one group with the preferred transfer syntax 72 if (hasPreferred && 73 sourceSyntaxes.find(preferred) == sourceSyntaxes.end()) 74 { 75 std::set<DicomTransferSyntax> group; 76 group.insert(preferred); 77 groups.push_back(group); 78 } 79 80 // Thirdly, add all the uncompressed transfer syntaxes as one single group 81 if (proposeUncompressedSyntaxes_) 82 { 83 static const size_t N = 3; 84 static const DicomTransferSyntax UNCOMPRESSED_SYNTAXES[N] = { 85 DicomTransferSyntax_LittleEndianImplicit, 86 DicomTransferSyntax_LittleEndianExplicit, 87 DicomTransferSyntax_BigEndianExplicit 88 }; 89 90 std::set<DicomTransferSyntax> group; 91 92 for (size_t i = 0; i < N; i++) 93 { 94 DicomTransferSyntax syntax = UNCOMPRESSED_SYNTAXES[i]; 95 if (sourceSyntaxes.find(syntax) == sourceSyntaxes.end() && 96 (!hasPreferred || preferred != syntax)) 97 { 98 group.insert(syntax); 99 } 100 } 101 102 if (!group.empty()) 103 { 104 groups.push_back(group); 105 } 106 } 107 108 // Now, propose each of these groups of transfer syntaxes 109 if (association_->GetRemainingPropositions() <= groups.size()) 110 { 111 return false; // Not enough room 112 } 113 else 114 { 115 for (GroupsOfSyntaxes::const_iterator it = groups.begin(); it != groups.end(); ++it) 116 { 117 association_->ProposePresentationContext(sopClassUid, *it); 118 119 // Remember the syntaxes that were individually proposed, in 120 // order to avoid renegociation if they are seen again (**) 121 if (it->size() == 1) 122 { 123 DicomTransferSyntax syntax = *it->begin(); 124 proposedOriginalClasses_.insert(std::make_pair(sopClassUid, syntax)); 125 } 126 } 127 128 return true; 129 } 130 } 131 132 LookupPresentationContext(uint8_t & presentationContextId,const std::string & sopClassUid,DicomTransferSyntax transferSyntax)133 bool DicomStoreUserConnection::LookupPresentationContext( 134 uint8_t& presentationContextId, 135 const std::string& sopClassUid, 136 DicomTransferSyntax transferSyntax) 137 { 138 typedef std::map<DicomTransferSyntax, uint8_t> PresentationContexts; 139 140 PresentationContexts pc; 141 if (association_->IsOpen() && 142 association_->LookupAcceptedPresentationContext(pc, sopClassUid)) 143 { 144 PresentationContexts::const_iterator found = pc.find(transferSyntax); 145 if (found != pc.end()) 146 { 147 presentationContextId = found->second; 148 return true; 149 } 150 } 151 152 return false; 153 } 154 155 DicomStoreUserConnection(const DicomAssociationParameters & params)156 DicomStoreUserConnection::DicomStoreUserConnection( 157 const DicomAssociationParameters& params) : 158 parameters_(params), 159 association_(new DicomAssociation), 160 proposeCommonClasses_(true), 161 proposeUncompressedSyntaxes_(true), 162 proposeRetiredBigEndian_(false) 163 { 164 } 165 GetParameters() const166 const DicomAssociationParameters &DicomStoreUserConnection::GetParameters() const 167 { 168 return parameters_; 169 } 170 SetCommonClassesProposed(bool proposed)171 void DicomStoreUserConnection::SetCommonClassesProposed(bool proposed) 172 { 173 proposeCommonClasses_ = proposed; 174 } 175 IsCommonClassesProposed() const176 bool DicomStoreUserConnection::IsCommonClassesProposed() const 177 { 178 return proposeCommonClasses_; 179 } 180 SetUncompressedSyntaxesProposed(bool proposed)181 void DicomStoreUserConnection::SetUncompressedSyntaxesProposed(bool proposed) 182 { 183 proposeUncompressedSyntaxes_ = proposed; 184 } 185 IsUncompressedSyntaxesProposed() const186 bool DicomStoreUserConnection::IsUncompressedSyntaxesProposed() const 187 { 188 return proposeUncompressedSyntaxes_; 189 } 190 SetRetiredBigEndianProposed(bool propose)191 void DicomStoreUserConnection::SetRetiredBigEndianProposed(bool propose) 192 { 193 proposeRetiredBigEndian_ = propose; 194 } 195 IsRetiredBigEndianProposed() const196 bool DicomStoreUserConnection::IsRetiredBigEndianProposed() const 197 { 198 return proposeRetiredBigEndian_; 199 } 200 201 RegisterStorageClass(const std::string & sopClassUid,DicomTransferSyntax syntax)202 void DicomStoreUserConnection::RegisterStorageClass(const std::string& sopClassUid, 203 DicomTransferSyntax syntax) 204 { 205 RegisteredClasses::iterator found = registeredClasses_.find(sopClassUid); 206 207 if (found == registeredClasses_.end()) 208 { 209 std::set<DicomTransferSyntax> ts; 210 ts.insert(syntax); 211 registeredClasses_[sopClassUid] = ts; 212 } 213 else 214 { 215 found->second.insert(syntax); 216 } 217 } 218 219 LookupParameters(std::string & sopClassUid,std::string & sopInstanceUid,DicomTransferSyntax & transferSyntax,DcmFileFormat & dicom)220 void DicomStoreUserConnection::LookupParameters(std::string& sopClassUid, 221 std::string& sopInstanceUid, 222 DicomTransferSyntax& transferSyntax, 223 DcmFileFormat& dicom) 224 { 225 if (dicom.getDataset() == NULL) 226 { 227 throw OrthancException(ErrorCode_InternalError); 228 } 229 230 OFString a, b; 231 if (!dicom.getDataset()->findAndGetOFString(DCM_SOPClassUID, a).good() || 232 !dicom.getDataset()->findAndGetOFString(DCM_SOPInstanceUID, b).good()) 233 { 234 throw OrthancException(ErrorCode_NoSopClassOrInstance, 235 "Unable to determine the SOP class/instance for C-STORE with AET " + 236 parameters_.GetRemoteModality().GetApplicationEntityTitle()); 237 } 238 239 sopClassUid.assign(a.c_str()); 240 sopInstanceUid.assign(b.c_str()); 241 242 if (!FromDcmtkBridge::LookupOrthancTransferSyntax(transferSyntax, dicom)) 243 { 244 throw OrthancException(ErrorCode_InternalError, 245 "Unknown transfer syntax from DCMTK"); 246 } 247 } 248 249 NegotiatePresentationContext(uint8_t & presentationContextId,const std::string & sopClassUid,DicomTransferSyntax transferSyntax,bool hasPreferred,DicomTransferSyntax preferred)250 bool DicomStoreUserConnection::NegotiatePresentationContext( 251 uint8_t& presentationContextId, 252 const std::string& sopClassUid, 253 DicomTransferSyntax transferSyntax, 254 bool hasPreferred, 255 DicomTransferSyntax preferred) 256 { 257 /** 258 * Step 1: Check whether this presentation context is already 259 * available in the previously negotiated assocation. 260 **/ 261 262 if (LookupPresentationContext(presentationContextId, sopClassUid, transferSyntax)) 263 { 264 return true; 265 } 266 267 // The association must be re-negotiated 268 if (association_->IsOpen()) 269 { 270 CLOG(INFO, DICOM) << "Re-negotiating DICOM association with " 271 << parameters_.GetRemoteModality().GetApplicationEntityTitle(); 272 273 // Don't renegociate if we know that the remote modality was 274 // already proposed this individual transfer syntax (**) 275 if (proposedOriginalClasses_.find(std::make_pair(sopClassUid, transferSyntax)) != 276 proposedOriginalClasses_.end()) 277 { 278 CLOG(INFO, DICOM) << "The remote modality has already rejected SOP class UID \"" 279 << sopClassUid << "\" with transfer syntax \"" 280 << GetTransferSyntaxUid(transferSyntax) << "\", don't renegotiate"; 281 return false; 282 } 283 } 284 285 association_->ClearPresentationContexts(); 286 proposedOriginalClasses_.clear(); 287 RegisterStorageClass(sopClassUid, transferSyntax); // (*) 288 289 290 /** 291 * Step 2: Propose at least the mandatory SOP class. 292 **/ 293 294 { 295 RegisteredClasses::const_iterator mandatory = registeredClasses_.find(sopClassUid); 296 297 if (mandatory == registeredClasses_.end() || 298 mandatory->second.find(transferSyntax) == mandatory->second.end()) 299 { 300 // Should never fail because of (*) 301 throw OrthancException(ErrorCode_InternalError); 302 } 303 304 if (!ProposeStorageClass(sopClassUid, mandatory->second, hasPreferred, preferred)) 305 { 306 // Should never happen in real life: There are no more than 307 // 128 transfer syntaxes in DICOM! 308 throw OrthancException(ErrorCode_InternalError, 309 "Too many transfer syntaxes for SOP class UID: " + sopClassUid); 310 } 311 } 312 313 314 /** 315 * Step 3: Propose all the previously spotted SOP classes, as 316 * registered through the "RegisterStorageClass()" method. 317 **/ 318 319 for (RegisteredClasses::const_iterator it = registeredClasses_.begin(); 320 it != registeredClasses_.end(); ++it) 321 { 322 if (it->first != sopClassUid) 323 { 324 ProposeStorageClass(it->first, it->second, hasPreferred, preferred); 325 } 326 } 327 328 329 /** 330 * Step 4: As long as there is room left in the proposed 331 * presentation contexts, propose the uncompressed transfer syntaxes 332 * for the most common SOP classes, as can be found in the 333 * "dcmShortSCUStorageSOPClassUIDs" array from DCMTK. The 334 * preferred transfer syntax is "LittleEndianImplicit". 335 **/ 336 337 if (proposeCommonClasses_) 338 { 339 // The method "ProposeStorageClass()" will automatically add 340 // "LittleEndianImplicit" 341 std::set<DicomTransferSyntax> ts; 342 343 for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs; i++) 344 { 345 std::string c(dcmShortSCUStorageSOPClassUIDs[i]); 346 347 if (c != sopClassUid && 348 registeredClasses_.find(c) == registeredClasses_.end()) 349 { 350 ProposeStorageClass(c, ts, hasPreferred, preferred); 351 } 352 } 353 } 354 355 356 /** 357 * Step 5: Open the association, and check whether the pair (SOP 358 * class UID, transfer syntax) was accepted by the remote host. 359 **/ 360 361 association_->Open(parameters_); 362 return LookupPresentationContext(presentationContextId, sopClassUid, transferSyntax); 363 } 364 365 Store(std::string & sopClassUid,std::string & sopInstanceUid,DcmFileFormat & dicom,bool hasMoveOriginator,const std::string & moveOriginatorAET,uint16_t moveOriginatorID)366 void DicomStoreUserConnection::Store(std::string& sopClassUid, 367 std::string& sopInstanceUid, 368 DcmFileFormat& dicom, 369 bool hasMoveOriginator, 370 const std::string& moveOriginatorAET, 371 uint16_t moveOriginatorID) 372 { 373 DicomTransferSyntax transferSyntax; 374 LookupParameters(sopClassUid, sopInstanceUid, transferSyntax, dicom); 375 376 uint8_t presID; 377 if (!NegotiatePresentationContext(presID, sopClassUid, transferSyntax, proposeUncompressedSyntaxes_, 378 DicomTransferSyntax_LittleEndianExplicit)) 379 { 380 throw OrthancException(ErrorCode_NetworkProtocol, 381 "No valid presentation context was negotiated for " 382 "SOP class UID [" + sopClassUid + "] and transfer " 383 "syntax [" + GetTransferSyntaxUid(transferSyntax) + "] " 384 "while sending to modality [" + 385 parameters_.GetRemoteModality().GetApplicationEntityTitle() + "]"); 386 } 387 388 // Prepare the transmission of data 389 T_DIMSE_C_StoreRQ request; 390 memset(&request, 0, sizeof(request)); 391 request.MessageID = association_->GetDcmtkAssociation().nextMsgID++; 392 strncpy(request.AffectedSOPClassUID, sopClassUid.c_str(), DIC_UI_LEN); 393 request.Priority = DIMSE_PRIORITY_MEDIUM; 394 request.DataSetType = DIMSE_DATASET_PRESENT; 395 strncpy(request.AffectedSOPInstanceUID, sopInstanceUid.c_str(), DIC_UI_LEN); 396 397 if (hasMoveOriginator) 398 { 399 strncpy(request.MoveOriginatorApplicationEntityTitle, 400 moveOriginatorAET.c_str(), DIC_AE_LEN); 401 request.opts = O_STORE_MOVEORIGINATORAETITLE; 402 403 request.MoveOriginatorID = moveOriginatorID; // The type DIC_US is an alias for uint16_t 404 request.opts |= O_STORE_MOVEORIGINATORID; 405 } 406 407 if (dicom.getDataset() == NULL) 408 { 409 throw OrthancException(ErrorCode_InternalError); 410 } 411 412 // Finally conduct transmission of data 413 T_DIMSE_C_StoreRSP response; 414 DcmDataset* statusDetail = NULL; 415 DicomAssociation::CheckCondition( 416 DIMSE_storeUser(&association_->GetDcmtkAssociation(), presID, &request, 417 NULL, dicom.getDataset(), ProgressCallback, NULL, 418 /*opt_blockMode*/ (GetParameters().HasTimeout() ? DIMSE_NONBLOCKING : DIMSE_BLOCKING), 419 /*opt_dimse_timeout*/ GetParameters().GetTimeout(), 420 &response, &statusDetail, NULL), 421 GetParameters(), "C-STORE"); 422 423 if (statusDetail != NULL) 424 { 425 delete statusDetail; 426 } 427 428 { 429 OFString str; 430 CLOG(TRACE, DICOM) << "Received Store Response:" << std::endl 431 << DIMSE_dumpMessage(str, response, DIMSE_INCOMING, NULL, presID); 432 } 433 434 /** 435 * New in Orthanc 1.6.0: Deal with failures during C-STORE. 436 * http://dicom.nema.org/medical/dicom/current/output/chtml/part04/sect_B.2.3.html#table_B.2-1 437 **/ 438 439 if (response.DimseStatus != 0x0000 && // Success 440 response.DimseStatus != 0xB000 && // Warning - Coercion of Data Elements 441 response.DimseStatus != 0xB007 && // Warning - Data Set does not match SOP Class 442 response.DimseStatus != 0xB006) // Warning - Elements Discarded 443 { 444 char buf[16]; 445 sprintf(buf, "%04X", response.DimseStatus); 446 throw OrthancException(ErrorCode_NetworkProtocol, 447 "C-STORE SCU to AET \"" + 448 GetParameters().GetRemoteModality().GetApplicationEntityTitle() + 449 "\" has failed with DIMSE status 0x" + buf); 450 } 451 } 452 453 Store(std::string & sopClassUid,std::string & sopInstanceUid,const void * buffer,size_t size,bool hasMoveOriginator,const std::string & moveOriginatorAET,uint16_t moveOriginatorID)454 void DicomStoreUserConnection::Store(std::string& sopClassUid, 455 std::string& sopInstanceUid, 456 const void* buffer, 457 size_t size, 458 bool hasMoveOriginator, 459 const std::string& moveOriginatorAET, 460 uint16_t moveOriginatorID) 461 { 462 std::unique_ptr<DcmFileFormat> dicom( 463 FromDcmtkBridge::LoadFromMemoryBuffer(buffer, size)); 464 465 if (dicom.get() == NULL) 466 { 467 throw OrthancException(ErrorCode_InternalError); 468 } 469 470 Store(sopClassUid, sopInstanceUid, *dicom, hasMoveOriginator, moveOriginatorAET, moveOriginatorID); 471 } 472 473 LookupTranscoding(std::set<DicomTransferSyntax> & acceptedSyntaxes,const std::string & sopClassUid,DicomTransferSyntax sourceSyntax,bool hasPreferred,DicomTransferSyntax preferred)474 void DicomStoreUserConnection::LookupTranscoding(std::set<DicomTransferSyntax>& acceptedSyntaxes, 475 const std::string& sopClassUid, 476 DicomTransferSyntax sourceSyntax, 477 bool hasPreferred, 478 DicomTransferSyntax preferred) 479 { 480 acceptedSyntaxes.clear(); 481 482 // Make sure a negotiation has already occurred for this transfer 483 // syntax. We don't use the return code: Transcoding is possible 484 // even if the "sourceSyntax" is not supported. 485 uint8_t presID; 486 NegotiatePresentationContext(presID, sopClassUid, sourceSyntax, hasPreferred, preferred); 487 488 std::map<DicomTransferSyntax, uint8_t> contexts; 489 if (association_->LookupAcceptedPresentationContext(contexts, sopClassUid)) 490 { 491 for (std::map<DicomTransferSyntax, uint8_t>::const_iterator 492 it = contexts.begin(); it != contexts.end(); ++it) 493 { 494 acceptedSyntaxes.insert(it->first); 495 } 496 } 497 } 498 499 Transcode(std::string & sopClassUid,std::string & sopInstanceUid,IDicomTranscoder & transcoder,const void * buffer,size_t size,DicomTransferSyntax preferredTransferSyntax,bool hasMoveOriginator,const std::string & moveOriginatorAET,uint16_t moveOriginatorID)500 void DicomStoreUserConnection::Transcode(std::string& sopClassUid /* out */, 501 std::string& sopInstanceUid /* out */, 502 IDicomTranscoder& transcoder, 503 const void* buffer, 504 size_t size, 505 DicomTransferSyntax preferredTransferSyntax, 506 bool hasMoveOriginator, 507 const std::string& moveOriginatorAET, 508 uint16_t moveOriginatorID) 509 { 510 std::unique_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(buffer, size)); 511 if (dicom.get() == NULL || 512 dicom->getDataset() == NULL) 513 { 514 throw OrthancException(ErrorCode_NullPointer); 515 } 516 517 DicomTransferSyntax sourceSyntax; 518 LookupParameters(sopClassUid, sopInstanceUid, sourceSyntax, *dicom); 519 520 std::set<DicomTransferSyntax> accepted; 521 LookupTranscoding(accepted, sopClassUid, sourceSyntax, true, preferredTransferSyntax); 522 523 if (accepted.find(sourceSyntax) != accepted.end()) 524 { 525 // No need for transcoding 526 Store(sopClassUid, sopInstanceUid, *dicom, 527 hasMoveOriginator, moveOriginatorAET, moveOriginatorID); 528 } 529 else 530 { 531 // Transcoding is needed 532 IDicomTranscoder::DicomImage source; 533 source.AcquireParsed(dicom.release()); 534 source.SetExternalBuffer(buffer, size); 535 536 const std::string sourceUid = IDicomTranscoder::GetSopInstanceUid(source.GetParsed()); 537 538 IDicomTranscoder::DicomImage transcoded; 539 bool success = false; 540 bool isDestructiveCompressionAllowed = false; 541 std::set<DicomTransferSyntax> attemptedSyntaxes; 542 543 if (accepted.find(preferredTransferSyntax) != accepted.end()) 544 { 545 // New in Orthanc 1.9.0: The preferred transfer syntax is 546 // accepted by the remote modality => transcode to this syntax 547 std::set<DicomTransferSyntax> targetSyntaxes; 548 targetSyntaxes.insert(preferredTransferSyntax); 549 attemptedSyntaxes.insert(preferredTransferSyntax); 550 551 success = transcoder.Transcode(transcoded, source, targetSyntaxes, true); 552 isDestructiveCompressionAllowed = true; 553 } 554 555 if (!success) 556 { 557 // Transcode to either one of the uncompressed transfer 558 // syntaxes that are accepted by the remote modality 559 560 std::set<DicomTransferSyntax> targetSyntaxes; 561 562 if (accepted.find(DicomTransferSyntax_LittleEndianImplicit) != accepted.end()) 563 { 564 targetSyntaxes.insert(DicomTransferSyntax_LittleEndianImplicit); 565 attemptedSyntaxes.insert(DicomTransferSyntax_LittleEndianImplicit); 566 } 567 568 if (accepted.find(DicomTransferSyntax_LittleEndianExplicit) != accepted.end()) 569 { 570 targetSyntaxes.insert(DicomTransferSyntax_LittleEndianExplicit); 571 attemptedSyntaxes.insert(DicomTransferSyntax_LittleEndianExplicit); 572 } 573 574 if (accepted.find(DicomTransferSyntax_BigEndianExplicit) != accepted.end()) 575 { 576 targetSyntaxes.insert(DicomTransferSyntax_BigEndianExplicit); 577 attemptedSyntaxes.insert(DicomTransferSyntax_BigEndianExplicit); 578 } 579 580 if (!targetSyntaxes.empty()) 581 { 582 success = transcoder.Transcode(transcoded, source, targetSyntaxes, false); 583 isDestructiveCompressionAllowed = false; 584 } 585 } 586 587 if (success) 588 { 589 std::string targetUid = IDicomTranscoder::GetSopInstanceUid(transcoded.GetParsed()); 590 if (sourceUid != targetUid) 591 { 592 if (isDestructiveCompressionAllowed) 593 { 594 LOG(WARNING) << "Because of the use of a preferred transfer syntax that corresponds to " 595 << "a destructive compression, C-STORE SCU has hanged the SOP Instance UID " 596 << "of a DICOM instance from \"" << sourceUid << "\" to \"" << targetUid << "\""; 597 } 598 else 599 { 600 throw OrthancException(ErrorCode_Plugin, "The transcoder has changed the SOP " 601 "Instance UID while transcoding to an uncompressed transfer syntax"); 602 } 603 } 604 605 DicomTransferSyntax transcodedSyntax; 606 607 // Sanity check 608 if (!FromDcmtkBridge::LookupOrthancTransferSyntax(transcodedSyntax, transcoded.GetParsed()) || 609 accepted.find(transcodedSyntax) == accepted.end()) 610 { 611 throw OrthancException(ErrorCode_InternalError); 612 } 613 else 614 { 615 Store(sopClassUid, sopInstanceUid, transcoded.GetParsed(), 616 hasMoveOriginator, moveOriginatorAET, moveOriginatorID); 617 } 618 } 619 else 620 { 621 std::string s; 622 for (std::set<DicomTransferSyntax>::const_iterator 623 it = attemptedSyntaxes.begin(); it != attemptedSyntaxes.end(); ++it) 624 { 625 s += " " + std::string(GetTransferSyntaxUid(*it)); 626 } 627 628 throw OrthancException(ErrorCode_NotImplemented, "Cannot transcode from " + 629 std::string(GetTransferSyntaxUid(sourceSyntax)) + 630 " to one of [" + s + " ]"); 631 } 632 } 633 } 634 635 Transcode(std::string & sopClassUid,std::string & sopInstanceUid,IDicomTranscoder & transcoder,const void * buffer,size_t size,bool hasMoveOriginator,const std::string & moveOriginatorAET,uint16_t moveOriginatorID)636 void DicomStoreUserConnection::Transcode(std::string& sopClassUid /* out */, 637 std::string& sopInstanceUid /* out */, 638 IDicomTranscoder& transcoder, 639 const void* buffer, 640 size_t size, 641 bool hasMoveOriginator, 642 const std::string& moveOriginatorAET, 643 uint16_t moveOriginatorID) 644 { 645 Transcode(sopClassUid, sopInstanceUid, transcoder, buffer, size, DicomTransferSyntax_LittleEndianExplicit, 646 hasMoveOriginator, moveOriginatorAET, moveOriginatorID); 647 } 648 } 649