使用dctmtk实现DICOM文件的发送(StoreSCU)
1 //===================================================================== 2 // SendDICOM.cpp : Defines the entry point for the DLL application. 3 // 4 // Created by HGB 2011 Nanjing ChunRen L.T.D 5 //===================================================================== 6 7 8 #include "stdafx.h" 9 #include "SendDICOM.h" 10 #include "osconfig.h" /* make sure OS specific configuration is included first */ 11 12 #define INCLUDE_CSTDLIB 13 #define INCLUDE_CSTDIO 14 #define INCLUDE_CSTRING 15 #define INCLUDE_CERRNO 16 #define INCLUDE_CSTDARG 17 #define INCLUDE_CCTYPE 18 #include "ofstdinc.h" 19 20 BEGIN_EXTERN_C 21 #ifdef HAVE_SYS_FILE_H 22 #include <sys/file.h> 23 #endif 24 END_EXTERN_C 25 26 #ifdef HAVE_GUSI_H 27 #include <GUSI.h> 28 #endif 29 30 #include "ofstring.h" 31 #include "dimse.h" 32 #include "diutil.h" 33 #include "dcdatset.h" 34 #include "dcmetinf.h" 35 #include "dcfilefo.h" 36 #include "dcdebug.h" 37 #include "dcuid.h" 38 #include "dcdict.h" 39 #include "dcdeftag.h" 40 #include "cmdlnarg.h" 41 #include "ofconapp.h" 42 #include "dcuid.h" /* for dcmtk version name */ 43 #include "dicom.h" /* for DICOM_APPLICATION_REQUESTOR */ 44 #include "dcostrmz.h" /* for dcmZlibCompressionLevel */ 45 #include "dcasccfg.h" /* for class DcmAssociationConfiguration */ 46 #include "dcasccff.h" /* for class DcmAssociationConfigurationFile */ 47 48 #ifdef ON_THE_FLY_COMPRESSION 49 #include "djdecode.h" /* for dcmjpeg decoders */ 50 #include "djencode.h" /* for dcmjpeg encoders */ 51 #include "dcrledrg.h" /* for DcmRLEDecoderRegistration */ 52 #include "dcrleerg.h" /* for DcmRLEEncoderRegistration */ 53 #endif 54 55 #ifdef WITH_OPENSSL 56 #include "tlstrans.h" 57 #include "tlslayer.h" 58 #endif 59 60 #include "WINSOCK.H" 61 62 #ifdef WITH_ZLIB 63 #include <zlib.h> /* for zlibVersion() */ 64 #endif 65 66 static E_TransferSyntax opt_networkTransferSyntax = EXS_Unknown; 67 static OFBool opt_proposeOnlyRequiredPresentationContexts = OFFalse; 68 static OFBool opt_combineProposedTransferSyntaxes = OFFalse; 69 static OFCmdUnsignedInt opt_repeatCount = 1; 70 static OFBool opt_haltOnUnsuccessfulStore = OFTrue; 71 static OFCmdUnsignedInt opt_inventPatientCount = 25; 72 static OFCmdUnsignedInt opt_inventStudyCount = 50; 73 static OFCmdUnsignedInt opt_inventSeriesCount = 100; 74 static OFBool opt_inventSOPInstanceInformation = OFFalse; 75 static OFBool opt_correctUIDPadding = OFFalse; 76 static OFBool unsuccessfulStoreEncountered = OFFalse; 77 static OFBool opt_verbose = OFFalse; 78 static OFBool opt_showPresentationContexts = OFFalse; 79 static OFBool opt_debug = OFFalse; 80 static OFBool opt_abortAssociation = OFFalse; 81 static OFCmdUnsignedInt opt_maxReceivePDULength = ASC_DEFAULTMAXPDU; 82 static OFCmdUnsignedInt opt_maxSendPDULength = 0; 83 T_DIMSE_BlockingMode opt_blockMode = DIMSE_BLOCKING; 84 int opt_dimse_timeout = 0; 85 int opt_acse_timeout = 30; 86 static int lastStatusCode = STATUS_Success; 87 static OFString studyIDPrefix("SID_"); // StudyID is SH (maximum 16 chars) 88 static OFString accessionNumberPrefix; // AccessionNumber is SH (maximum 16 chars) 89 static OFString patientIDPrefix("PID_"); // PatientID is LO (maximum 64 chars) 90 static OFString patientNamePrefix("OFFIS^TEST_PN_"); // PatientName is PN (maximum 16 chars) 91 92 static OFCondition 93 addStoragePresentationContexts(T_ASC_Parameters *params, OFList<OFString>& sopClasses); 94 static OFCondition 95 cstore(T_ASC_Association * assoc, const OFString& fname); 96 static OFBool 97 isaListMember(OFList<OFString>& lst, OFString& s); 98 99 static OFCondition 100 addPresentationContext(T_ASC_Parameters *params, 101 int presentationContextId, const OFString& abstractSyntax, 102 const OFList<OFString>& transferSyntaxList, 103 T_ASC_SC_ROLE proposedRole = ASC_SC_ROLE_DEFAULT); 104 static OFCondition 105 addPresentationContext(T_ASC_Parameters *params, 106 int presentationContextId, const OFString& abstractSyntax, 107 const OFString& transferSyntax, 108 T_ASC_SC_ROLE proposedRole = ASC_SC_ROLE_DEFAULT); 109 static OFCondition 110 storeSCU(T_ASC_Association * assoc, const char *fname); 111 static void 112 replaceSOPInstanceInformation(DcmDataset* dataset); 113 static void 114 progressCallback(void * /*callbackData*/, 115 T_DIMSE_StoreProgress *progress, 116 T_DIMSE_C_StoreRQ * /*req*/); 117 static OFString 118 makeUID(OFString basePrefix, int counter); 119 static int 120 secondsSince1970(); 121 static OFString 122 intToString(int i); 123 static OFBool 124 updateStringAttributeValue(DcmItem* dataset, const DcmTagKey& key, OFString& value); 125 126 BOOL APIENTRY DllMain( HANDLE hModule, 127 DWORD ul_reason_for_call, 128 LPVOID lpReserved 129 ) 130 { 131 return TRUE; 132 } 133 134 int IncInt(int params) 135 { 136 return params+1; 137 } 138 139 //int IniNet 140 141 /*==========================================================*/ 142 //Created by hgb 20061229 143 //result value: 144 //0: success 145 //-1: not foud file 146 //-2: 147 //-3: 148 //-4: 149 //-5: 150 /*==========================================================*/ 151 int __stdcall SendDCM(LPSTR ourTitle, LPSTR peerTitle, 152 LPSTR scpIP, LPSTR scpPort, LPSTR FileName) 153 { 154 char sopClassUID[128]; 155 char sopInstanceUID[128]; 156 OFList<OFString> fileNameList; 157 OFList<OFString> sopClassUIDList; 158 OFList<OFString> sopInstanceUIDList; 159 T_ASC_Network *net; 160 T_ASC_Parameters *params; 161 DIC_NODENAME localHost; 162 DIC_NODENAME peerHost; 163 T_ASC_Association *assoc; 164 165 //TCHAR tcsModulePath[_MAX_PATH]; 166 //::GetModuleFileName(NULL, tcsModulePath, _MAX_PATH); 167 168 //CString strCurDir = tcsModulePath; 169 //strCurDir = strCurDir.Left(strCurDir.ReverseFind(TEXT('\\'))+1); 170 //char currentFilename[strCurDir.GetLength()+1]; 171 //strcpy(currentFilename, strCurDir.GetBuffer()); 172 //char *currentFilename = strCurDir; 173 174 175 #ifdef HAVE_GUSI_H 176 GUSISetup(GUSIwithSIOUXSockets); 177 GUSISetup(GUSIwithInternetSockets); 178 #endif 179 180 #ifdef HAVE_WINSOCK_H 181 WSAData winSockData; 182 /* we need at least version 1.1 */ 183 WORD winSockVersionNeeded = MAKEWORD( 1, 1 ); 184 WSAStartup(winSockVersionNeeded, &winSockData); 185 #endif 186 187 /* 188 CFileFind find; 189 if(find.FindFile(FileName)) 190 { 191 return -1; // not found the file 192 } 193 */ 194 195 196 if (access(FileName, R_OK)!=0) 197 return -2; // did't access file 198 if (!DU_findSOPClassAndInstanceInFile(FileName, sopClassUID, sopInstanceUID)) 199 return -101; 200 if (!dcmIsaStorageSOPClassUID(sopClassUID)) 201 return -102; 202 else 203 { 204 //fileNameList.push_back(FileName); 205 sopClassUIDList.push_back(sopClassUID); 206 sopInstanceUIDList.push_back(sopInstanceUID); 207 208 OFCondition cond = ASC_initializeNetwork(NET_REQUESTOR, 0, 30, &net); 209 if (cond.bad()) 210 return -103; 211 212 cond = ASC_createAssociationParameters(¶ms, ASC_DEFAULTMAXPDU); 213 if (cond.bad()) 214 return -104; 215 216 ASC_setAPTitles(params, ourTitle, peerTitle, NULL); 217 gethostname(localHost, sizeof(localHost) - 1); 218 sprintf(peerHost, "%s:%s", scpIP, scpPort);//////// 219 ASC_setPresentationAddresses(params, localHost, peerHost); 220 221 cond = addStoragePresentationContexts(params, sopClassUIDList); 222 if (cond.bad()) 223 { 224 return -105; 225 } 226 227 cond = ASC_requestAssociation(net, params, &assoc); 228 if (cond.bad()) 229 { 230 if (cond == DUL_ASSOCIATIONREJECTED) { 231 return -106; 232 } else { //Association Request Failed 233 return -107; 234 } 235 } 236 237 //发送文件 238 cond = EC_Normal; 239 //OFListIterator(OFString) iter = fileNameList.begin(); 240 //OFListIterator(OFString) enditer = fileNameList.end();//// 241 //cond = cstore(assoc, *iter); //OFString 242 cond = cstore(assoc, OFString(FileName)); 243 if (cond != EC_Normal) 244 { 245 //ASC_releaseAssociation(assoc); 246 //ASC_destroyAssociation(&assoc); 247 //DimseCondition::dump(cond); 248 //#ifdef HAVE_WINSOCK_H 249 //WSACleanup(); 250 //#endif 251 return -108;//send faid; 252 } 253 254 cond = ASC_releaseAssociation(assoc); 255 if (cond.bad()) 256 return -109; 257 258 cond = ASC_destroyAssociation(&assoc); 259 if (cond.bad()) 260 return -120; 261 262 } 263 264 #ifdef HAVE_WINSOCK_H 265 WSACleanup(); 266 #endif 267 return 0; 268 269 } 270 271 272 static OFCondition 273 addStoragePresentationContexts(T_ASC_Parameters *params, OFList<OFString>& sopClasses) 274 { 275 /* 276 * Each SOP Class will be proposed in two presentation contexts (unless 277 * the opt_combineProposedTransferSyntaxes global variable is true). 278 * The command line specified a preferred transfer syntax to use. 279 * This prefered transfer syntax will be proposed in one 280 * presentation context and a set of alternative (fallback) transfer 281 * syntaxes will be proposed in a different presentation context. 282 * 283 * Generally, we prefer to use Explicitly encoded transfer syntaxes 284 * and if running on a Little Endian machine we prefer 285 * LittleEndianExplicitTransferSyntax to BigEndianTransferSyntax. 286 * Some SCP implementations will just select the first transfer 287 * syntax they support (this is not part of the standard) so 288 * organise the proposed transfer syntaxes to take advantage 289 * of such behaviour. 290 */ 291 292 // Which transfer syntax was preferred on the command line 293 OFString preferredTransferSyntax; 294 if (opt_networkTransferSyntax == EXS_Unknown) { 295 /* gLocalByteOrder is defined in dcxfer.h */ 296 if (gLocalByteOrder == EBO_LittleEndian) { 297 /* we are on a little endian machine */ 298 preferredTransferSyntax = UID_LittleEndianExplicitTransferSyntax; 299 } else { 300 /* we are on a big endian machine */ 301 preferredTransferSyntax = UID_BigEndianExplicitTransferSyntax; 302 } 303 } else { 304 DcmXfer xfer(opt_networkTransferSyntax); 305 preferredTransferSyntax = xfer.getXferID(); 306 } 307 308 OFListIterator(OFString) s_cur; 309 OFListIterator(OFString) s_end; 310 311 312 OFList<OFString> fallbackSyntaxes; 313 fallbackSyntaxes.push_back(UID_LittleEndianExplicitTransferSyntax); 314 fallbackSyntaxes.push_back(UID_BigEndianExplicitTransferSyntax); 315 fallbackSyntaxes.push_back(UID_LittleEndianImplicitTransferSyntax); 316 // Remove the preferred syntax from the fallback list 317 fallbackSyntaxes.remove(preferredTransferSyntax); 318 // If little endian implicit is preferred then we don't need any fallback syntaxes 319 // because it is the default transfer syntax and all applications must support it. 320 if (opt_networkTransferSyntax == EXS_LittleEndianImplicit) { 321 fallbackSyntaxes.clear(); 322 } 323 324 // created a list of transfer syntaxes combined from the preferred and fallback syntaxes 325 OFList<OFString> combinedSyntaxes; 326 s_cur = fallbackSyntaxes.begin(); 327 s_end = fallbackSyntaxes.end(); 328 combinedSyntaxes.push_back(preferredTransferSyntax); 329 while (s_cur != s_end) 330 { 331 if (!isaListMember(combinedSyntaxes, *s_cur)) combinedSyntaxes.push_back(*s_cur); 332 ++s_cur; 333 } 334 335 if (!opt_proposeOnlyRequiredPresentationContexts) { 336 // add the (short list of) known storage sop classes to the list 337 // the array of Storage SOP Class UIDs comes from dcuid.h 338 for (int i=0; i<numberOfDcmShortSCUStorageSOPClassUIDs; i++) { 339 sopClasses.push_back(dcmShortSCUStorageSOPClassUIDs[i]); 340 } 341 } 342 343 // thin out the sop classes to remove any duplicates. 344 OFList<OFString> sops; 345 s_cur = sopClasses.begin(); 346 s_end = sopClasses.end(); 347 while (s_cur != s_end) { 348 if (!isaListMember(sops, *s_cur)) { 349 sops.push_back(*s_cur); 350 } 351 ++s_cur; 352 } 353 354 // add a presentations context for each sop class / transfer syntax pair 355 OFCondition cond = EC_Normal; 356 int pid = 1; // presentation context id 357 s_cur = sops.begin(); 358 s_end = sops.end(); 359 while (s_cur != s_end && cond.good()) { 360 361 if (pid > 255) { 362 ///errmsg("Too many presentation contexts"); 363 return ASC_BADPRESENTATIONCONTEXTID; 364 } 365 366 if (opt_combineProposedTransferSyntaxes) { 367 cond = addPresentationContext(params, pid, *s_cur, combinedSyntaxes); 368 pid += 2; /* only odd presentation context id's */ 369 } else { 370 371 // sop class with preferred transfer syntax 372 cond = addPresentationContext(params, pid, *s_cur, preferredTransferSyntax); 373 pid += 2; /* only odd presentation context id's */ 374 375 if (fallbackSyntaxes.size() > 0) { 376 if (pid > 255) { 377 //errmsg("Too many presentation contexts"); 378 return ASC_BADPRESENTATIONCONTEXTID; 379 } 380 381 // sop class with fallback transfer syntax 382 cond = addPresentationContext(params, pid, *s_cur, fallbackSyntaxes); 383 pid += 2; /* only odd presentation context id's */ 384 } 385 } 386 ++s_cur; 387 } 388 389 return cond; 390 } 391 392 static OFCondition 393 cstore(T_ASC_Association * assoc, const OFString& fname) 394 /* 395 * This function will process the given file as often as is specified by opt_repeatCount. 396 * "Process" in this case means "read file, send C-STORE-RQ, receive C-STORE-RSP". 397 * 398 * Parameters: 399 * assoc - [in] The association (network connection to another DICOM application). 400 * fname - [in] Name of the file which shall be processed. 401 */ 402 { 403 OFCondition cond = EC_Normal; 404 405 /* opt_repeatCount specifies how many times a certain file shall be processed */ 406 int n = (int)opt_repeatCount; 407 408 /* as long as no error occured and the counter does not equal 0 */ 409 while ((cond.good()) && n-- && !(opt_haltOnUnsuccessfulStore && unsuccessfulStoreEncountered)) 410 { 411 /* process file (read file, send C-STORE-RQ, receive C-STORE-RSP) */ 412 cond = storeSCU(assoc, fname.c_str()); 413 } 414 415 // we don't want to return an error code if --no-halt was specified. 416 if (! opt_haltOnUnsuccessfulStore) 417 { 418 cond = EC_Normal; 419 } 420 421 /* return result value */ 422 return cond; 423 } 424 425 static OFBool 426 isaListMember(OFList<OFString>& lst, OFString& s) 427 { 428 OFListIterator(OFString) cur = lst.begin(); 429 OFListIterator(OFString) end = lst.end(); 430 431 OFBool found = OFFalse; 432 433 while (cur != end && !found) { 434 435 found = (s == *cur); 436 437 ++cur; 438 } 439 440 return found; 441 } 442 443 static OFCondition 444 addPresentationContext(T_ASC_Parameters *params, 445 int presentationContextId, const OFString& abstractSyntax, 446 const OFString& transferSyntax, 447 T_ASC_SC_ROLE proposedRole) 448 { 449 const char* c_p = transferSyntax.c_str(); 450 OFCondition cond = ASC_addPresentationContext(params, presentationContextId, 451 abstractSyntax.c_str(), &c_p, 1, proposedRole); 452 return cond; 453 } 454 455 static OFCondition 456 addPresentationContext(T_ASC_Parameters *params, 457 int presentationContextId, const OFString& abstractSyntax, 458 const OFList<OFString>& transferSyntaxList, 459 T_ASC_SC_ROLE proposedRole) 460 { 461 // create an array of supported/possible transfer syntaxes 462 const char** transferSyntaxes = new const char*[transferSyntaxList.size()]; 463 int transferSyntaxCount = 0; 464 OFListConstIterator(OFString) s_cur = transferSyntaxList.begin(); 465 OFListConstIterator(OFString) s_end = transferSyntaxList.end(); 466 while (s_cur != s_end) { 467 transferSyntaxes[transferSyntaxCount++] = (*s_cur).c_str(); 468 ++s_cur; 469 } 470 471 OFCondition cond = ASC_addPresentationContext(params, presentationContextId, 472 abstractSyntax.c_str(), transferSyntaxes, transferSyntaxCount, proposedRole); 473 474 delete[] transferSyntaxes; 475 return cond; 476 } 477 478 static OFCondition 479 storeSCU(T_ASC_Association * assoc, const char *fname) 480 /* 481 * This function will read all the information from the given file, 482 * figure out a corresponding presentation context which will be used 483 * to transmit the information over the network to the SCP, and it 484 * will finally initiate the transmission of all data to the SCP. 485 * 486 * Parameters: 487 * assoc - [in] The association (network connection to another DICOM application). 488 * fname - [in] Name of the file which shall be processed. 489 */ 490 { 491 DIC_US msgId = assoc->nextMsgID++; 492 T_ASC_PresentationContextID presId; 493 T_DIMSE_C_StoreRQ req; 494 T_DIMSE_C_StoreRSP rsp; 495 DIC_UI sopClass; 496 DIC_UI sopInstance; 497 DcmDataset *statusDetail = NULL; 498 499 unsuccessfulStoreEncountered = OFTrue; // assumption 500 501 if (opt_verbose) { 502 printf("--------------------------\n"); 503 printf("Sending file: %s\n", fname); 504 } 505 506 /* read information from file. After the call to DcmFileFormat::loadFile(...) the information */ 507 /* which is encapsulated in the file will be available through the DcmFileFormat object. */ 508 /* In detail, it will be available through calls to DcmFileFormat::getMetaInfo() (for */ 509 /* meta header information) and DcmFileFormat::getDataset() (for data set information). */ 510 DcmFileFormat dcmff; 511 OFCondition cond = dcmff.loadFile(fname); 512 513 /* figure out if an error occured while the file was read*/ 514 if (cond.bad()) { 515 //errmsg("Bad DICOM file: %s: %s", fname, cond.text()); 516 return cond; 517 } 518 519 /* if required, invent new SOP instance information for the current data set (user option) */ 520 if (opt_inventSOPInstanceInformation) { 521 replaceSOPInstanceInformation(dcmff.getDataset()); 522 } 523 524 /* figure out which SOP class and SOP instance is encapsulated in the file */ 525 if (!DU_findSOPClassAndInstanceInDataSet(dcmff.getDataset(), 526 sopClass, sopInstance, opt_correctUIDPadding)) { 527 //errmsg("No SOP Class & Instance UIDs in file: %s", fname); 528 return DIMSE_BADDATA; 529 } 530 531 /* figure out which of the accepted presentation contexts should be used */ 532 DcmXfer filexfer(dcmff.getDataset()->getOriginalXfer());//??????? added by HGB 533 534 /* special case: if the file uses an unencapsulated transfer syntax (uncompressed 535 * or deflated explicit VR) and we prefer deflated explicit VR, then try 536 * to find a presentation context for deflated explicit VR first. 537 */ 538 if (filexfer.isNotEncapsulated() && 539 opt_networkTransferSyntax == EXS_DeflatedLittleEndianExplicit) 540 { 541 filexfer = EXS_DeflatedLittleEndianExplicit; 542 } 543 544 if (filexfer.getXfer() != EXS_Unknown) presId = ASC_findAcceptedPresentationContextID(assoc, sopClass, filexfer.getXferID()); 545 else presId = ASC_findAcceptedPresentationContextID(assoc, sopClass); 546 if (presId == 0) { 547 const char *modalityName = dcmSOPClassUIDToModality(sopClass); 548 if (!modalityName) modalityName = dcmFindNameOfUID(sopClass); 549 if (!modalityName) modalityName = "unknown SOP class"; 550 // errmsg("No presentation context for: (%s) %s", modalityName, sopClass); 551 return DIMSE_NOVALIDPRESENTATIONCONTEXTID; 552 } 553 554 /* if required, dump general information concerning transfer syntaxes */ 555 if (opt_verbose) { 556 DcmXfer fileTransfer(dcmff.getDataset()->getOriginalXfer()); 557 T_ASC_PresentationContext pc; 558 ASC_findAcceptedPresentationContext(assoc->params, presId, &pc); 559 DcmXfer netTransfer(pc.acceptedTransferSyntax); 560 printf("Transfer: %s -> %s\n", 561 dcmFindNameOfUID(fileTransfer.getXferID()), dcmFindNameOfUID(netTransfer.getXferID())); 562 } 563 564 /* prepare the transmission of data */ 565 bzero((char*)&req, sizeof(req)); 566 req.MessageID = msgId; 567 strcpy(req.AffectedSOPClassUID, sopClass); 568 strcpy(req.AffectedSOPInstanceUID, sopInstance); 569 req.DataSetType = DIMSE_DATASET_PRESENT; 570 req.Priority = DIMSE_PRIORITY_LOW; 571 572 /* if required, dump some more general information */ 573 if (opt_verbose) { 574 printf("Store SCU RQ: MsgID %d, (%s)\n", msgId, dcmSOPClassUIDToModality(sopClass)); 575 } 576 577 /* finally conduct transmission of data */ 578 cond = DIMSE_storeUser(assoc, presId, &req, 579 NULL, dcmff.getDataset(), progressCallback, NULL, 580 opt_blockMode, opt_dimse_timeout, 581 &rsp, &statusDetail, NULL, DU_fileSize(fname)); 582 583 /* 584 * If store command completed normally, with a status 585 * of success or some warning then the image was accepted. 586 */ 587 if (cond == EC_Normal && (rsp.DimseStatus == STATUS_Success || DICOM_WARNING_STATUS(rsp.DimseStatus))) { 588 unsuccessfulStoreEncountered = OFFalse; 589 } 590 591 /* remember the response's status for later transmissions of data */ 592 lastStatusCode = rsp.DimseStatus; 593 594 /* dump some more general information */ 595 if (cond == EC_Normal) 596 { 597 if (opt_verbose) { 598 DIMSE_printCStoreRSP(stdout, &rsp); 599 } 600 } 601 else 602 { 603 //errmsg("Store Failed, file: %s:", fname); 604 DimseCondition::dump(cond); 605 } 606 607 /* dump status detail information if there is some */ 608 if (statusDetail != NULL) { 609 printf(" Status Detail:\n"); 610 statusDetail->print(COUT); 611 delete statusDetail; 612 } 613 /* return */ 614 return cond; 615 } 616 617 static void 618 replaceSOPInstanceInformation(DcmDataset* dataset) 619 { 620 static OFCmdUnsignedInt patientCounter = 0; 621 static OFCmdUnsignedInt studyCounter = 0; 622 static OFCmdUnsignedInt seriesCounter = 0; 623 static OFCmdUnsignedInt imageCounter = 0; 624 static OFString seriesInstanceUID; 625 static OFString seriesNumber; 626 static OFString studyInstanceUID; 627 static OFString studyID; 628 static OFString accessionNumber; 629 static OFString patientID; 630 static OFString patientName; 631 632 if (seriesInstanceUID.length() == 0) seriesInstanceUID=makeUID(SITE_SERIES_UID_ROOT, (int)seriesCounter); 633 if (seriesNumber.length() == 0) seriesNumber = intToString((int)seriesCounter); 634 if (studyInstanceUID.length() == 0) studyInstanceUID = makeUID(SITE_STUDY_UID_ROOT, (int)studyCounter); 635 if (studyID.length() == 0) studyID = studyIDPrefix + intToString((int)secondsSince1970()) + intToString((int)studyCounter); 636 if (accessionNumber.length() == 0) accessionNumber = accessionNumberPrefix + intToString(secondsSince1970()) + intToString((int)studyCounter); 637 if (patientID.length() == 0) patientID = patientIDPrefix + intToString(secondsSince1970()) + intToString((int)patientCounter); 638 if (patientName.length() == 0) patientName = patientNamePrefix + intToString(secondsSince1970()) + intToString((int)patientCounter); 639 640 if (imageCounter >= opt_inventSeriesCount) { 641 imageCounter = 0; 642 seriesCounter++; 643 seriesInstanceUID = makeUID(SITE_SERIES_UID_ROOT, (int)seriesCounter); 644 seriesNumber = intToString((int)seriesCounter); 645 } 646 if (seriesCounter >= opt_inventStudyCount) { 647 seriesCounter = 0; 648 studyCounter++; 649 studyInstanceUID = makeUID(SITE_STUDY_UID_ROOT, (int)studyCounter); 650 studyID = studyIDPrefix + intToString(secondsSince1970()) + intToString((int)studyCounter); 651 accessionNumber = accessionNumberPrefix + intToString(secondsSince1970()) + intToString((int)studyCounter); 652 } 653 if (studyCounter >= opt_inventPatientCount) { 654 // we create as many patients as necessary */ 655 studyCounter = 0; 656 patientCounter++; 657 patientID = patientIDPrefix + intToString(secondsSince1970()) + intToString((int)patientCounter); 658 patientName = patientNamePrefix + intToString(secondsSince1970()) + intToString((int)patientCounter); 659 } 660 661 OFString sopInstanceUID = makeUID(SITE_INSTANCE_UID_ROOT, (int)imageCounter); 662 OFString imageNumber = intToString((int)imageCounter); 663 664 if (opt_verbose) { 665 COUT << "Inventing Identifying Information (" << 666 "pa" << patientCounter << ", st" << studyCounter << 667 ", se" << seriesCounter << ", im" << imageCounter << "): " << endl; 668 COUT << " PatientName=" << patientName << endl; 669 COUT << " PatientID=" << patientID << endl; 670 COUT << " StudyInstanceUID=" << studyInstanceUID << endl; 671 COUT << " StudyID=" << studyID << endl; 672 COUT << " SeriesInstanceUID=" << seriesInstanceUID << endl; 673 COUT << " SeriesNumber=" << seriesNumber << endl; 674 COUT << " SOPInstanceUID=" << sopInstanceUID << endl; 675 COUT << " ImageNumber=" << imageNumber << endl; 676 } 677 678 updateStringAttributeValue(dataset, DCM_PatientsName, patientName); 679 updateStringAttributeValue(dataset, DCM_PatientID, patientID); 680 updateStringAttributeValue(dataset, DCM_StudyInstanceUID, studyInstanceUID); 681 updateStringAttributeValue(dataset, DCM_StudyID, studyID); 682 updateStringAttributeValue(dataset, DCM_SeriesInstanceUID, seriesInstanceUID); 683 updateStringAttributeValue(dataset, DCM_SeriesNumber, seriesNumber); 684 updateStringAttributeValue(dataset, DCM_SOPInstanceUID, sopInstanceUID); 685 updateStringAttributeValue(dataset, DCM_InstanceNumber, imageNumber); 686 687 imageCounter++; 688 } 689 690 static void 691 progressCallback(void * /*callbackData*/, 692 T_DIMSE_StoreProgress *progress, 693 T_DIMSE_C_StoreRQ * /*req*/) 694 { 695 if (opt_verbose) { 696 switch (progress->state) { 697 case DIMSE_StoreBegin: 698 printf("XMIT:"); break; 699 case DIMSE_StoreEnd: 700 printf("\n"); break; 701 default: 702 putchar('.'); break; 703 } 704 fflush(stdout); 705 } 706 } 707 708 static OFString 709 makeUID(OFString basePrefix, int counter) 710 { 711 OFString prefix = basePrefix + "." + intToString(counter); 712 char uidbuf[65]; 713 OFString uid = dcmGenerateUniqueIdentifier(uidbuf, prefix.c_str()); 714 return uid; 715 } 716 717 static int 718 secondsSince1970() 719 { 720 time_t t = time(NULL); 721 return (int)t; 722 } 723 724 static OFString 725 intToString(int i) 726 { 727 char numbuf[32]; 728 sprintf(numbuf, "%d", i); 729 return numbuf; 730 } 731 732 static OFBool 733 updateStringAttributeValue(DcmItem* dataset, const DcmTagKey& key, OFString& value) 734 { 735 DcmStack stack; 736 DcmTag tag(key); 737 738 OFCondition cond = EC_Normal; 739 cond = dataset->search(key, stack, ESM_fromHere, OFFalse); 740 if (cond != EC_Normal) { 741 CERR << "error: updateStringAttributeValue: cannot find: " << tag.getTagName() 742 << " " << key << ": " 743 << cond.text() << endl; 744 return OFFalse; 745 } 746 747 DcmElement* elem = (DcmElement*) stack.top(); 748 749 DcmVR vr(elem->ident()); 750 if (elem->getLength() > vr.getMaxValueLength()) { 751 CERR << "error: updateStringAttributeValue: INTERNAL ERROR: " << tag.getTagName() 752 << " " << key << ": value too large (max " 753 << vr.getMaxValueLength() << ") for " << vr.getVRName() << " value: " << value << endl; 754 return OFFalse; 755 } 756 757 cond = elem->putOFStringArray(value); 758 if (cond != EC_Normal) { 759 CERR << "error: updateStringAttributeValue: cannot put string in attribute: " << tag.getTagName() 760 << " " << key << ": " 761 << cond.text() << endl; 762 return OFFalse; 763 } 764 765 return OFTrue; 766 }