ctkDICOMSCP.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. #include "ctkDICOMSCP.h"
  2. // DCMTK includes
  3. #include <dcmtk/dcmqrdb/dcmqrsrv.h>
  4. // Qt includes
  5. #include <QtConcurrentRun>
  6. // CTK includes
  7. #include "ctkLogger.h"
  8. static ctkLogger logger ( "org.commontk.dicom.DcmSCP" );
  9. class ctkDICOMSCPPrivate
  10. {
  11. public:
  12. ctkDICOMSCPPrivate(ctkDICOMSCP* parent);
  13. ~ctkDICOMSCPPrivate();
  14. QString aeTitle;
  15. int port;
  16. int maxPDU;
  17. int maxAssociation;
  18. int acseTimeout;
  19. bool isGetEnabled;
  20. bool isPatientRootEnabled;
  21. bool isStudyRootEnabled;
  22. bool isPatientStudyOnlyEnabled;
  23. bool isShutdownAllowed;
  24. bool isThreadingEnabled;
  25. bool terminateRequested;
  26. /** wait for incoming A-ASSOCIATE requests, perform association negotiation
  27. * and serve the requests. May fork child processes depending on availability
  28. * of the fork() system function and configuration options.
  29. * @param theNet network structure for listen socket
  30. * @return EC_Normal if successful, an error code otherwise
  31. */
  32. OFCondition waitForAssociation(T_ASC_Network *theNet);
  33. OFCondition negotiateAssociation(T_ASC_Association * assoc);
  34. OFCondition refuseAssociation(T_ASC_Association ** assoc, CTN_RefuseReason reason);
  35. OFCondition handleAssociation(T_ASC_Association * assoc, OFBool correctUIDPadding);
  36. OFCondition dispatch(T_ASC_Association *assoc, OFBool correctUIDPadding);
  37. void threadedStartServer();
  38. void threadedHandleAssociation(T_ASC_Association * assoc,
  39. OFBool correctUIDPadding);
  40. QMap<T_DIMSE_Command, ctkDICOMSCP*> registeredSCPs;
  41. T_ASC_Network* network;
  42. private:
  43. ctkDICOMSCP* q_ptr;
  44. Q_DECLARE_PUBLIC(ctkDICOMSCP);
  45. };
  46. //------------------------------------------------------------------------------
  47. // ctkDICOMSCPPrivate class methods
  48. //------------------------------------------------------------------------------
  49. //------------------------------------------------------------------------------
  50. ctkDICOMSCPPrivate::ctkDICOMSCPPrivate(ctkDICOMSCP *parent):
  51. q_ptr(parent)
  52. {
  53. this->aeTitle = "AETitle";
  54. this->port = 104;
  55. this->maxPDU = 100000;
  56. this->maxAssociation = 20;
  57. this->acseTimeout = 30;
  58. this->isGetEnabled = false;
  59. this->isPatientRootEnabled = false;
  60. this->isStudyRootEnabled = true;
  61. this->isPatientStudyOnlyEnabled = false;
  62. this->isShutdownAllowed = true;
  63. this->terminateRequested = false;
  64. this->isThreadingEnabled = true;
  65. this->network = new T_ASC_Network();
  66. }
  67. //------------------------------------------------------------------------------
  68. ctkDICOMSCPPrivate::~ctkDICOMSCPPrivate()
  69. {
  70. delete this->network;
  71. }
  72. //------------------------------------------------------------------------------
  73. OFCondition ctkDICOMSCPPrivate::waitForAssociation(T_ASC_Network * theNet)
  74. {
  75. Q_Q(ctkDICOMSCP);
  76. OFCondition cond = EC_Normal;
  77. OFString temp_str;
  78. T_ASC_Association *assoc;
  79. char buf[BUFSIZ];
  80. int timeout = 1000;
  81. OFBool go_cleanup = OFFalse;
  82. if (ASC_associationWaiting(theNet, timeout))
  83. {
  84. cond = ASC_receiveAssociation(theNet, &assoc, this->maxPDU);
  85. if (cond.bad())
  86. {
  87. DimseCondition::dump(temp_str, cond);
  88. logger.debug(QString("Failed to receive association: ") + temp_str.c_str());
  89. go_cleanup = OFTrue;
  90. }
  91. } else return EC_Normal;
  92. if (! go_cleanup)
  93. {
  94. logger.info(QString("Association Received (") + assoc->params->DULparams.callingPresentationAddress
  95. + ":" + assoc->params->DULparams.callingAPTitle + " -> "
  96. + assoc->params->DULparams.calledAPTitle + ")");
  97. ASC_dumpParameters(temp_str, assoc->params, ASC_ASSOC_RQ);
  98. logger.debug(QString("Parameters:") + temp_str.c_str());
  99. }
  100. if (! go_cleanup)
  101. {
  102. /* Application Context Name */
  103. cond = ASC_getApplicationContextName(assoc->params, buf);
  104. if (cond.bad() || strcmp(buf, DICOM_STDAPPLICATIONCONTEXT) != 0)
  105. {
  106. /* reject: the application context name is not supported */
  107. logger.info(QString("Bad AppContextName: ") + buf);
  108. cond = this->refuseAssociation(&assoc, CTN_BadAppContext);
  109. go_cleanup = OFTrue;
  110. }
  111. }
  112. if (! go_cleanup)
  113. {
  114. /* Implementation Class UID */
  115. if (strlen(assoc->params->theirImplementationClassUID) == 0)
  116. {
  117. /* reject: no implementation Class UID provided */
  118. logger.info(QString("No implementation Class UID provided"));
  119. cond = this->refuseAssociation(&assoc, CTN_NoReason);
  120. go_cleanup = OFTrue;
  121. }
  122. }
  123. // TODO: Check whether peer is in AETitle list
  124. /*
  125. if (! go_cleanup)
  126. {
  127. // Does peer AE have access to required service ??
  128. if (! config_->peerInAETitle(assoc->params->DULparams.calledAPTitle,
  129. assoc->params->DULparams.callingAPTitle,
  130. assoc->params->DULparams.callingPresentationAddress))
  131. {
  132. DCMQRDB_DEBUG("Peer "
  133. << assoc->params->DULparams.callingPresentationAddress << ":"
  134. << assoc->params->DULparams.callingAPTitle << " is not not permitted to access "
  135. << assoc->params->DULparams.calledAPTitle << " (see configuration file)");
  136. cond = refuseAssociation(&assoc, CTN_BadAEService);
  137. go_cleanup = OFTrue;
  138. }
  139. }
  140. */
  141. // TODO: Check if the number of associations exceeds the maximum number of associations
  142. /*
  143. if (! go_cleanup)
  144. {
  145. // too many concurrent associations ??
  146. if (processtable_.countChildProcesses() >= OFstatic_cast(size_t, d->maxAssociation))
  147. {
  148. cond = d->refuseAssociation(&assoc, CTN_TooManyAssociations);
  149. go_cleanup = OFTrue;
  150. }
  151. }
  152. */
  153. if (! go_cleanup)
  154. {
  155. cond = this->negotiateAssociation(assoc);
  156. if (cond.bad()) go_cleanup = OFTrue;
  157. }
  158. if (! go_cleanup)
  159. {
  160. cond = ASC_acknowledgeAssociation(assoc);
  161. if (cond.bad())
  162. {
  163. DimseCondition::dump(temp_str, cond);
  164. logger.error(QString(temp_str.c_str()));
  165. go_cleanup = OFTrue;
  166. }
  167. }
  168. if (! go_cleanup)
  169. {
  170. logger.debug(QString("Association Acknowledged (Max Send PDV: ") + QString::number(assoc->sendPDVLength) + ")");
  171. QtConcurrent::run(this, &ctkDICOMSCPPrivate::threadedHandleAssociation, assoc, false);
  172. return EC_Normal;
  173. }
  174. // cleanup code
  175. OFCondition oldcond = cond; /* store condition flag for later use */
  176. if (cond != ASC_SHUTDOWNAPPLICATION)
  177. {
  178. /* the child will handle the association, we can drop it */
  179. cond = ASC_dropAssociation(assoc);
  180. if (cond.bad())
  181. {
  182. DimseCondition::dump(temp_str, cond);
  183. logger.error(QString("Cannot Drop Association: ") + temp_str.c_str());
  184. }
  185. cond = ASC_destroyAssociation(&assoc);
  186. if (cond.bad())
  187. {
  188. DimseCondition::dump(temp_str, cond);
  189. logger.error(QString("Cannot Destroy Association: ") + temp_str.c_str());
  190. }
  191. }
  192. if (oldcond == ASC_SHUTDOWNAPPLICATION) cond = oldcond; /* abort flag is reported to top-level wait loop */
  193. return cond;
  194. }
  195. //------------------------------------------------------------------------------
  196. OFCondition ctkDICOMSCPPrivate::negotiateAssociation(T_ASC_Association * assoc)
  197. {
  198. OFCondition cond = EC_Normal;
  199. int i;
  200. OFString temp_str;
  201. struct { const char *moveSyntax, *findSyntax; } queryRetrievePairs[] =
  202. {
  203. { UID_MOVEPatientRootQueryRetrieveInformationModel,
  204. UID_FINDPatientRootQueryRetrieveInformationModel },
  205. { UID_MOVEStudyRootQueryRetrieveInformationModel,
  206. UID_FINDStudyRootQueryRetrieveInformationModel },
  207. { UID_RETIRED_MOVEPatientStudyOnlyQueryRetrieveInformationModel,
  208. UID_RETIRED_FINDPatientStudyOnlyQueryRetrieveInformationModel }
  209. };
  210. DIC_AE calledAETitle;
  211. ASC_getAPTitles(assoc->params, NULL, calledAETitle, NULL);
  212. const char* transferSyntaxes[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  213. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
  214. transferSyntaxes[0] = UID_LittleEndianExplicitTransferSyntax;
  215. transferSyntaxes[1] = UID_BigEndianExplicitTransferSyntax;
  216. transferSyntaxes[2] = UID_LittleEndianImplicitTransferSyntax;
  217. int numTransferSyntaxes = 3;
  218. const char * const nonStorageSyntaxes[] =
  219. {
  220. UID_VerificationSOPClass,
  221. UID_FINDPatientRootQueryRetrieveInformationModel,
  222. UID_MOVEPatientRootQueryRetrieveInformationModel,
  223. UID_GETPatientRootQueryRetrieveInformationModel,
  224. #ifndef NO_PATIENTSTUDYONLY_SUPPORT
  225. UID_RETIRED_FINDPatientStudyOnlyQueryRetrieveInformationModel,
  226. UID_RETIRED_MOVEPatientStudyOnlyQueryRetrieveInformationModel,
  227. UID_RETIRED_GETPatientStudyOnlyQueryRetrieveInformationModel,
  228. #endif
  229. UID_FINDStudyRootQueryRetrieveInformationModel,
  230. UID_MOVEStudyRootQueryRetrieveInformationModel,
  231. UID_GETStudyRootQueryRetrieveInformationModel,
  232. UID_PrivateShutdownSOPClass
  233. };
  234. const int numberOfNonStorageSyntaxes = DIM_OF(nonStorageSyntaxes);
  235. const char *selectedNonStorageSyntaxes[DIM_OF(nonStorageSyntaxes)];
  236. int numberOfSelectedNonStorageSyntaxes = 0;
  237. for (i = 0; i < numberOfNonStorageSyntaxes; i++)
  238. {
  239. if (0 == strcmp(nonStorageSyntaxes[i], UID_FINDPatientRootQueryRetrieveInformationModel))
  240. {
  241. if (this->isPatientRootEnabled) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
  242. }
  243. else if (0 == strcmp(nonStorageSyntaxes[i], UID_MOVEPatientRootQueryRetrieveInformationModel))
  244. {
  245. if (this->isPatientRootEnabled) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
  246. }
  247. else if (0 == strcmp(nonStorageSyntaxes[i], UID_GETPatientRootQueryRetrieveInformationModel))
  248. {
  249. if (this->isPatientRootEnabled && (this->isGetEnabled)) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
  250. }
  251. else if (0 == strcmp(nonStorageSyntaxes[i], UID_RETIRED_FINDPatientStudyOnlyQueryRetrieveInformationModel))
  252. {
  253. if (this->isPatientStudyOnlyEnabled) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
  254. }
  255. else if (0 == strcmp(nonStorageSyntaxes[i], UID_RETIRED_MOVEPatientStudyOnlyQueryRetrieveInformationModel))
  256. {
  257. if (this->isPatientStudyOnlyEnabled) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
  258. }
  259. else if (0 == strcmp(nonStorageSyntaxes[i], UID_RETIRED_GETPatientStudyOnlyQueryRetrieveInformationModel))
  260. {
  261. if (this->isPatientStudyOnlyEnabled && (this->isGetEnabled)) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
  262. }
  263. else if (0 == strcmp(nonStorageSyntaxes[i], UID_FINDStudyRootQueryRetrieveInformationModel))
  264. {
  265. if (this->isStudyRootEnabled) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
  266. }
  267. else if (0 == strcmp(nonStorageSyntaxes[i], UID_MOVEStudyRootQueryRetrieveInformationModel))
  268. {
  269. if (this->isStudyRootEnabled) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
  270. }
  271. else if (0 == strcmp(nonStorageSyntaxes[i], UID_GETStudyRootQueryRetrieveInformationModel))
  272. {
  273. if (this->isStudyRootEnabled && (this->isGetEnabled)) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
  274. }
  275. else if (0 == strcmp(nonStorageSyntaxes[i], UID_PrivateShutdownSOPClass))
  276. {
  277. if (this->isShutdownAllowed) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
  278. } else {
  279. selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
  280. }
  281. }
  282. /* accept any of the non-storage syntaxes */
  283. cond = ASC_acceptContextsWithPreferredTransferSyntaxes(
  284. assoc->params,
  285. (const char**)selectedNonStorageSyntaxes, numberOfSelectedNonStorageSyntaxes,
  286. (const char**)transferSyntaxes, numTransferSyntaxes);
  287. if (cond.bad()) {
  288. DimseCondition::dump(temp_str, cond);
  289. logger.error(QString("Cannot accept presentation contexts: ") + temp_str.c_str());
  290. }
  291. /* accept any of the storage syntaxes */
  292. if (!this->isGetEnabled)
  293. {
  294. /* accept storage syntaxes with default role only */
  295. cond = ASC_acceptContextsWithPreferredTransferSyntaxes(
  296. assoc->params,
  297. dcmAllStorageSOPClassUIDs, numberOfAllDcmStorageSOPClassUIDs,
  298. (const char**)transferSyntaxes, DIM_OF(transferSyntaxes));
  299. if (cond.bad()) {
  300. DimseCondition::dump(temp_str, cond);
  301. logger.error(QString("Cannot accept presentation contexts: ") + temp_str.c_str());
  302. }
  303. } else {
  304. /* accept storage syntaxes with proposed role */
  305. T_ASC_PresentationContext pc;
  306. T_ASC_SC_ROLE role;
  307. int npc = ASC_countPresentationContexts(assoc->params);
  308. for (i = 0; i < npc; i++)
  309. {
  310. ASC_getPresentationContext(assoc->params, i, &pc);
  311. if (dcmIsaStorageSOPClassUID(pc.abstractSyntax))
  312. {
  313. /*
  314. ** We are prepared to accept whatever role he proposes.
  315. ** Normally we can be the SCP of the Storage Service Class.
  316. ** When processing the C-GET operation we can be the SCU of the Storage Service Class.
  317. */
  318. role = pc.proposedRole;
  319. /*
  320. ** Accept in the order "least wanted" to "most wanted" transfer
  321. ** syntax. Accepting a transfer syntax will override previously
  322. ** accepted transfer syntaxes.
  323. */
  324. for (int k = numTransferSyntaxes - 1; k >= 0; k--)
  325. {
  326. for (int j = 0; j < (int)pc.transferSyntaxCount; j++)
  327. {
  328. /* if the transfer syntax was proposed then we can accept it
  329. * appears in our supported list of transfer syntaxes
  330. */
  331. if (strcmp(pc.proposedTransferSyntaxes[j], transferSyntaxes[k]) == 0)
  332. {
  333. cond = ASC_acceptPresentationContext(
  334. assoc->params, pc.presentationContextID, transferSyntaxes[k], role);
  335. if (cond.bad()) return cond;
  336. }
  337. }
  338. }
  339. }
  340. } /* for */
  341. } /* else */
  342. /*
  343. * check if we have negotiated the private "shutdown" SOP Class
  344. */
  345. if (0 != ASC_findAcceptedPresentationContextID(assoc, UID_PrivateShutdownSOPClass))
  346. {
  347. logger.info(QString("Shutting down server ... (negotiated private \"shut down\" SOP class)"));
  348. this->refuseAssociation(&assoc, CTN_NoReason);
  349. return ASC_SHUTDOWNAPPLICATION;
  350. }
  351. return cond;
  352. }
  353. //------------------------------------------------------------------------------
  354. void ctkDICOMSCPPrivate::threadedStartServer()
  355. {
  356. OFCondition cond = ASC_initializeNetwork(NET_ACCEPTORREQUESTOR, this->port, this->acseTimeout, &this->network);
  357. if(cond.bad()) {
  358. logger.error(QString("Failed to initialize network"));
  359. }
  360. while(!this->terminateRequested && cond.good())
  361. {
  362. cond = this->waitForAssociation(this->network);
  363. }
  364. cond = ASC_dropNetwork(&this->network);
  365. if(cond.bad()) {
  366. logger.debug(QString("Cannot drop network"));
  367. }
  368. this->terminateRequested = false;
  369. }
  370. //------------------------------------------------------------------------------
  371. void ctkDICOMSCPPrivate::threadedHandleAssociation(T_ASC_Association *assoc, bool correctUIDPadding)
  372. {
  373. this->handleAssociation(assoc, correctUIDPadding);
  374. }
  375. //------------------------------------------------------------------------------
  376. OFCondition ctkDICOMSCPPrivate::refuseAssociation(T_ASC_Association ** assoc, CTN_RefuseReason reason)
  377. {
  378. OFCondition cond = EC_Normal;
  379. T_ASC_RejectParameters rej;
  380. OFString temp_str;
  381. const char *reason_string;
  382. switch (reason)
  383. {
  384. case CTN_TooManyAssociations:
  385. reason_string = "TooManyAssociations";
  386. break;
  387. case CTN_CannotFork:
  388. reason_string = "CannotFork";
  389. break;
  390. case CTN_BadAppContext:
  391. reason_string = "BadAppContext";
  392. break;
  393. case CTN_BadAEPeer:
  394. reason_string = "BadAEPeer";
  395. break;
  396. case CTN_BadAEService:
  397. reason_string = "BadAEService";
  398. break;
  399. case CTN_NoReason:
  400. reason_string = "NoReason";
  401. break;
  402. default:
  403. reason_string = "???";
  404. break;
  405. }
  406. logger.info(QString("Refusing Association (") + reason_string + ")");
  407. switch (reason)
  408. {
  409. case CTN_TooManyAssociations:
  410. rej.result = ASC_RESULT_REJECTEDTRANSIENT;
  411. rej.source = ASC_SOURCE_SERVICEPROVIDER_PRESENTATION_RELATED;
  412. rej.reason = ASC_REASON_SP_PRES_LOCALLIMITEXCEEDED;
  413. break;
  414. case CTN_CannotFork:
  415. rej.result = ASC_RESULT_REJECTEDPERMANENT;
  416. rej.source = ASC_SOURCE_SERVICEPROVIDER_PRESENTATION_RELATED;
  417. rej.reason = ASC_REASON_SP_PRES_TEMPORARYCONGESTION;
  418. break;
  419. case CTN_BadAppContext:
  420. rej.result = ASC_RESULT_REJECTEDTRANSIENT;
  421. rej.source = ASC_SOURCE_SERVICEUSER;
  422. rej.reason = ASC_REASON_SU_APPCONTEXTNAMENOTSUPPORTED;
  423. break;
  424. case CTN_BadAEPeer:
  425. rej.result = ASC_RESULT_REJECTEDPERMANENT;
  426. rej.source = ASC_SOURCE_SERVICEUSER;
  427. rej.reason = ASC_REASON_SU_CALLINGAETITLENOTRECOGNIZED;
  428. break;
  429. case CTN_BadAEService:
  430. rej.result = ASC_RESULT_REJECTEDPERMANENT;
  431. rej.source = ASC_SOURCE_SERVICEUSER;
  432. rej.reason = ASC_REASON_SU_CALLEDAETITLENOTRECOGNIZED;
  433. break;
  434. case CTN_NoReason:
  435. default:
  436. rej.result = ASC_RESULT_REJECTEDPERMANENT;
  437. rej.source = ASC_SOURCE_SERVICEUSER;
  438. rej.reason = ASC_REASON_SU_NOREASON;
  439. break;
  440. }
  441. cond = ASC_rejectAssociation(*assoc, &rej);
  442. if (cond.bad())
  443. {
  444. DimseCondition::dump(temp_str, cond);
  445. logger.error(QString("Association Reject Failed: ") + temp_str.c_str());
  446. }
  447. cond = ASC_dropAssociation(*assoc);
  448. if (cond.bad())
  449. {
  450. DimseCondition::dump(temp_str, cond);
  451. logger.error(QString("Cannot Drop Association: ") + temp_str.c_str());
  452. }
  453. cond = ASC_destroyAssociation(assoc);
  454. if (cond.bad())
  455. {
  456. DimseCondition::dump(temp_str, cond);
  457. logger.error(QString("Cannot Destroy Association: ") + temp_str.c_str());
  458. }
  459. return cond;
  460. }
  461. //------------------------------------------------------------------------------
  462. OFCondition ctkDICOMSCPPrivate::handleAssociation(T_ASC_Association * assoc, OFBool correctUIDPadding)
  463. {
  464. OFCondition cond = EC_Normal;
  465. DIC_NODENAME peerHostName;
  466. DIC_AE peerAETitle;
  467. DIC_AE myAETitle;
  468. OFString temp_str;
  469. ASC_getPresentationAddresses(assoc->params, peerHostName, NULL);
  470. ASC_getAPTitles(assoc->params, peerAETitle, myAETitle, NULL);
  471. /* now do the real work */
  472. cond = dispatch(assoc, correctUIDPadding);
  473. /* clean up on association termination */
  474. if (cond == DUL_PEERREQUESTEDRELEASE) {
  475. logger.info("Association Release");
  476. cond = ASC_acknowledgeRelease(assoc);
  477. ASC_dropSCPAssociation(assoc);
  478. } else if (cond == DUL_PEERABORTEDASSOCIATION) {
  479. logger.info("Association Aborted");
  480. } else {
  481. DimseCondition::dump(temp_str, cond);
  482. logger.error(QString("DIMSE Failure (aborting association): ") + temp_str.c_str());
  483. /* some kind of error so abort the association */
  484. cond = ASC_abortAssociation(assoc);
  485. }
  486. cond = ASC_dropAssociation(assoc);
  487. if (cond.bad()) {
  488. DimseCondition::dump(temp_str, cond);
  489. logger.error(QString("Cannot Drop Association: ") + temp_str.c_str());
  490. }
  491. cond = ASC_destroyAssociation(&assoc);
  492. if (cond.bad()) {
  493. DimseCondition::dump(temp_str, cond);
  494. logger.error(QString("Cannot Destroy Association: ") + temp_str.c_str());
  495. }
  496. return cond;
  497. }
  498. //------------------------------------------------------------------------------
  499. OFCondition ctkDICOMSCPPrivate::dispatch(T_ASC_Association *assoc, OFBool correctUIDPadding)
  500. {
  501. OFCondition cond = EC_Normal;
  502. T_DIMSE_Message msg;
  503. T_ASC_PresentationContextID presID;
  504. // this while loop is executed exactly once unless the "keepDBHandleDuringAssociation_"
  505. // flag is not set, in which case the inner loop is executed only once and this loop
  506. // repeats for each incoming DIMSE command. In this case, the DB handle is created
  507. // and released for each DIMSE command.
  508. while (cond.good())
  509. {
  510. // this while loop is executed exactly once unless the "keepDBHandleDuringAssociation_"
  511. // flag is set, in which case the DB handle remains open until something goes wrong
  512. // or the remote peer closes the association
  513. while (cond.good())
  514. {
  515. cond = DIMSE_receiveCommand(assoc, DIMSE_BLOCKING, 0, &presID, &msg, NULL);
  516. /* did peer release, abort, or do we have a valid message ? */
  517. if (cond.good())
  518. {
  519. /* process command */
  520. if(msg.CommandField == DIMSE_C_CANCEL_RQ)
  521. {
  522. logger.info("dispatch: late C-CANCEL-RQ, ignoring");
  523. }
  524. else
  525. {
  526. if(this->registeredSCPs.contains(msg.CommandField))
  527. {
  528. cond = this->registeredSCPs[msg.CommandField]->handleRequest(assoc, msg, presID);
  529. }
  530. else
  531. {
  532. /* we cannot handle this kind of message */
  533. cond = DIMSE_BADCOMMANDTYPE;
  534. logger.error(QString("Cannot handle command: ") + (unsigned)msg.CommandField);
  535. }
  536. }
  537. }
  538. else if ((cond == DUL_PEERREQUESTEDRELEASE)||(cond == DUL_PEERABORTEDASSOCIATION))
  539. {
  540. // association gone
  541. }
  542. else
  543. {
  544. // the condition will be returned, the caller will abort the assosiation.
  545. }
  546. }
  547. }
  548. // Association done
  549. return cond;
  550. }
  551. //------------------------------------------------------------------------------
  552. // ctkDICOMSCP class methods
  553. //------------------------------------------------------------------------------
  554. //------------------------------------------------------------------------------
  555. ctkDICOMSCP::ctkDICOMSCP(QObject *parent) :
  556. QObject(parent),
  557. d_ptr(new ctkDICOMSCPPrivate(this))
  558. {
  559. this->dimseCommand = DIMSE_NOTHING;
  560. }
  561. //------------------------------------------------------------------------------
  562. ctkDICOMSCP::~ctkDICOMSCP()
  563. {
  564. delete d_ptr;
  565. }
  566. //------------------------------------------------------------------------------
  567. T_DIMSE_Command ctkDICOMSCP::getDimseCommand()
  568. {
  569. return this->dimseCommand;
  570. }
  571. //------------------------------------------------------------------------------
  572. bool ctkDICOMSCP::registerSCP(ctkDICOMSCP *scp)
  573. {
  574. Q_D(ctkDICOMSCP);
  575. if(!scp)
  576. {
  577. return false;
  578. }
  579. T_DIMSE_Command command = scp->getDimseCommand();
  580. if(d->registeredSCPs.contains(command))
  581. {
  582. return false;
  583. }
  584. scp->setParent(this);
  585. d->registeredSCPs[command] = scp;
  586. return true;
  587. }
  588. //------------------------------------------------------------------------------
  589. void ctkDICOMSCP::setAETitle(const QString& aeTitle)
  590. {
  591. Q_D(ctkDICOMSCP);
  592. d->aeTitle = aeTitle;
  593. }
  594. //------------------------------------------------------------------------------
  595. void ctkDICOMSCP::setPort(int port)
  596. {
  597. Q_D(ctkDICOMSCP);
  598. d->port = port;
  599. }
  600. //------------------------------------------------------------------------------
  601. void ctkDICOMSCP::setEnableThreading(bool flag)
  602. {
  603. Q_D(ctkDICOMSCP);
  604. d->isThreadingEnabled = flag;
  605. }
  606. //------------------------------------------------------------------------------
  607. void ctkDICOMSCP::setMaxPDU(int size)
  608. {
  609. Q_D(ctkDICOMSCP);
  610. d->maxPDU = size;
  611. }
  612. //------------------------------------------------------------------------------
  613. void ctkDICOMSCP::setMaxAssociations(int count)
  614. {
  615. Q_D(ctkDICOMSCP);
  616. d->maxAssociation = count;
  617. }
  618. //------------------------------------------------------------------------------
  619. void ctkDICOMSCP::setEnableGetSupport(bool flag)
  620. {
  621. Q_D(ctkDICOMSCP);
  622. d->isGetEnabled = flag;
  623. }
  624. //------------------------------------------------------------------------------
  625. void ctkDICOMSCP::setEnablePatientRoot(bool flag)
  626. {
  627. Q_D(ctkDICOMSCP);
  628. d->isPatientRootEnabled = flag;
  629. }
  630. //------------------------------------------------------------------------------
  631. void ctkDICOMSCP::setEnableStudyRoot(bool flag)
  632. {
  633. Q_D(ctkDICOMSCP);
  634. d->isStudyRootEnabled = flag;
  635. }
  636. //------------------------------------------------------------------------------
  637. void ctkDICOMSCP::start()
  638. {
  639. Q_D(ctkDICOMSCP);
  640. if(d->isThreadingEnabled)
  641. {
  642. QtConcurrent::run(d, &ctkDICOMSCPPrivate::threadedStartServer);
  643. }
  644. else
  645. {
  646. d->threadedStartServer();
  647. }
  648. }
  649. //------------------------------------------------------------------------------
  650. void ctkDICOMSCP::stop()
  651. {
  652. Q_D(ctkDICOMSCP);
  653. d->terminateRequested = true;
  654. }