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 }