Mercurial > hg > orthanc
annotate OrthancServer/DicomProtocol/DicomUserConnection.cpp @ 656:08eca5d86aad
fixes to cppcheck
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 04 Nov 2013 11:19:31 +0100 |
parents | 1b2cdc855bd3 |
children | 5425bb6f1ea5 |
rev | line source |
---|---|
0 | 1 /** |
62 | 2 * Orthanc - A Lightweight, RESTful DICOM Store |
399 | 3 * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, |
0 | 4 * Belgium |
5 * | |
6 * This program is free software: you can redistribute it and/or | |
7 * modify it under the terms of the GNU General Public License as | |
8 * published by the Free Software Foundation, either version 3 of the | |
9 * License, or (at your option) any later version. | |
136 | 10 * |
11 * In addition, as a special exception, the copyright holders of this | |
12 * program give permission to link the code of its release with the | |
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
14 * that use the same license as the "OpenSSL" library), and distribute | |
15 * the linked executables. You must obey the GNU General Public License | |
16 * in all respects for all of the code used other than "OpenSSL". If you | |
17 * modify file(s) with this exception, you may extend this exception to | |
18 * your version of the file(s), but you are not obligated to do so. If | |
19 * you do not wish to do so, delete this exception statement from your | |
20 * version. If you delete this exception statement from all source files | |
21 * in the program, then also delete it here. | |
0 | 22 * |
23 * This program is distributed in the hope that it will be useful, but | |
24 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
26 * General Public License for more details. | |
27 * | |
28 * You should have received a copy of the GNU General Public License | |
29 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
30 **/ | |
31 | |
32 | |
33 #include "DicomUserConnection.h" | |
34 | |
62 | 35 #include "../../Core/OrthancException.h" |
0 | 36 #include "../ToDcmtkBridge.h" |
37 #include "../FromDcmtkBridge.h" | |
38 | |
39 #include <dcmtk/dcmdata/dcistrmb.h> | |
40 #include <dcmtk/dcmdata/dcistrmf.h> | |
41 #include <dcmtk/dcmdata/dcfilefo.h> | |
42 #include <dcmtk/dcmnet/diutil.h> | |
43 | |
44 #include <set> | |
45 | |
46 | |
47 | |
48 #ifdef _WIN32 | |
49 /** | |
50 * "The maximum length, in bytes, of the string returned in the buffer | |
51 * pointed to by the name parameter is dependent on the namespace provider, | |
52 * but this string must be 256 bytes or less. | |
53 * http://msdn.microsoft.com/en-us/library/windows/desktop/ms738527(v=vs.85).aspx | |
54 **/ | |
55 #define HOST_NAME_MAX 256 | |
56 #endif | |
57 | |
58 | |
62 | 59 namespace Orthanc |
0 | 60 { |
61 struct DicomUserConnection::PImpl | |
62 { | |
63 // Connection state | |
64 T_ASC_Network* net_; | |
65 T_ASC_Parameters* params_; | |
66 T_ASC_Association* assoc_; | |
67 | |
68 bool IsOpen() const | |
69 { | |
70 return assoc_ != NULL; | |
71 } | |
72 | |
73 void CheckIsOpen() const; | |
74 | |
75 void Store(DcmInputStream& is); | |
76 }; | |
77 | |
78 | |
79 static void Check(const OFCondition& cond) | |
80 { | |
81 if (cond.bad()) | |
82 { | |
62 | 83 throw OrthancException("DicomUserConnection: " + std::string(cond.text())); |
0 | 84 } |
85 } | |
86 | |
87 void DicomUserConnection::PImpl::CheckIsOpen() const | |
88 { | |
89 if (!IsOpen()) | |
90 { | |
62 | 91 throw OrthancException("DicomUserConnection: First open the connection"); |
0 | 92 } |
93 } | |
94 | |
95 | |
96 void DicomUserConnection::CheckIsOpen() const | |
97 { | |
98 pimpl_->CheckIsOpen(); | |
99 } | |
100 | |
101 | |
102 void DicomUserConnection::CopyParameters(const DicomUserConnection& other) | |
103 { | |
104 Close(); | |
105 localAet_ = other.localAet_; | |
106 distantAet_ = other.distantAet_; | |
107 distantHost_ = other.distantHost_; | |
108 distantPort_ = other.distantPort_; | |
109 } | |
110 | |
111 | |
112 void DicomUserConnection::SetupPresentationContexts() | |
113 { | |
114 // The preferred abstract syntax | |
115 std::string preferredSyntax = UID_LittleEndianImplicitTransferSyntax; | |
116 | |
117 // Fallback abstract syntaxes | |
118 std::set<std::string> abstractSyntaxes; | |
119 abstractSyntaxes.insert(UID_LittleEndianExplicitTransferSyntax); | |
120 abstractSyntaxes.insert(UID_BigEndianExplicitTransferSyntax); | |
121 abstractSyntaxes.insert(UID_LittleEndianImplicitTransferSyntax); | |
122 abstractSyntaxes.erase(preferredSyntax); | |
123 assert(abstractSyntaxes.size() == 2); | |
124 | |
125 // Transfer syntaxes for C-ECHO, C-FIND and C-MOVE | |
126 std::vector<std::string> transferSyntaxes; | |
127 transferSyntaxes.push_back(UID_VerificationSOPClass); | |
128 transferSyntaxes.push_back(UID_FINDPatientRootQueryRetrieveInformationModel); | |
129 transferSyntaxes.push_back(UID_FINDStudyRootQueryRetrieveInformationModel); | |
130 transferSyntaxes.push_back(UID_MOVEStudyRootQueryRetrieveInformationModel); | |
131 | |
132 // TODO: Allow the set below to be configured | |
133 std::set<std::string> uselessSyntaxes; | |
134 uselessSyntaxes.insert(UID_BlendingSoftcopyPresentationStateStorage); | |
135 uselessSyntaxes.insert(UID_GrayscaleSoftcopyPresentationStateStorage); | |
136 uselessSyntaxes.insert(UID_ColorSoftcopyPresentationStateStorage); | |
137 uselessSyntaxes.insert(UID_PseudoColorSoftcopyPresentationStateStorage); | |
138 | |
139 // Add the transfer syntaxes for C-STORE | |
140 for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs - 1; i++) | |
141 { | |
142 // Test to make some room to allow the ECHO and FIND requests | |
143 if (uselessSyntaxes.find(dcmShortSCUStorageSOPClassUIDs[i]) == uselessSyntaxes.end()) | |
144 { | |
145 transferSyntaxes.push_back(dcmShortSCUStorageSOPClassUIDs[i]); | |
146 } | |
147 } | |
148 | |
149 // Flatten the fallback abstract syntaxes array | |
150 const char* asPreferred[1] = { preferredSyntax.c_str() }; | |
151 const char* asFallback[2]; | |
152 std::set<std::string>::const_iterator it = abstractSyntaxes.begin(); | |
153 asFallback[0] = it->c_str(); | |
656 | 154 ++it; |
0 | 155 asFallback[1] = it->c_str(); |
156 | |
157 unsigned int presentationContextId = 1; | |
158 for (size_t i = 0; i < transferSyntaxes.size(); i++) | |
159 { | |
160 Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, | |
161 transferSyntaxes[i].c_str(), asPreferred, 1)); | |
162 presentationContextId += 2; | |
163 | |
164 Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, | |
165 transferSyntaxes[i].c_str(), asFallback, 2)); | |
166 presentationContextId += 2; | |
167 } | |
168 } | |
169 | |
170 | |
171 void DicomUserConnection::PImpl::Store(DcmInputStream& is) | |
172 { | |
173 CheckIsOpen(); | |
174 | |
175 DcmFileFormat dcmff; | |
176 Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength)); | |
177 | |
178 // Figure out which SOP class and SOP instance is encapsulated in the file | |
179 DIC_UI sopClass; | |
180 DIC_UI sopInstance; | |
181 if (!DU_findSOPClassAndInstanceInDataSet(dcmff.getDataset(), sopClass, sopInstance)) | |
182 { | |
62 | 183 throw OrthancException("DicomUserConnection: Unable to find the SOP class and instance"); |
0 | 184 } |
185 | |
186 // Figure out which of the accepted presentation contexts should be used | |
187 int presID = ASC_findAcceptedPresentationContextID(assoc_, sopClass); | |
188 if (presID == 0) | |
189 { | |
190 const char *modalityName = dcmSOPClassUIDToModality(sopClass); | |
191 if (!modalityName) modalityName = dcmFindNameOfUID(sopClass); | |
192 if (!modalityName) modalityName = "unknown SOP class"; | |
62 | 193 throw OrthancException("DicomUserConnection: No presentation context for modality " + |
0 | 194 std::string(modalityName)); |
195 } | |
196 | |
197 // Prepare the transmission of data | |
198 T_DIMSE_C_StoreRQ req; | |
199 memset(&req, 0, sizeof(req)); | |
200 req.MessageID = assoc_->nextMsgID++; | |
201 strcpy(req.AffectedSOPClassUID, sopClass); | |
202 strcpy(req.AffectedSOPInstanceUID, sopInstance); | |
203 req.DataSetType = DIMSE_DATASET_PRESENT; | |
204 req.Priority = DIMSE_PRIORITY_MEDIUM; | |
205 | |
206 // Finally conduct transmission of data | |
207 T_DIMSE_C_StoreRSP rsp; | |
208 DcmDataset* statusDetail = NULL; | |
209 Check(DIMSE_storeUser(assoc_, presID, &req, | |
210 NULL, dcmff.getDataset(), /*progressCallback*/ NULL, NULL, | |
211 /*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ 0, | |
212 &rsp, &statusDetail, NULL)); | |
213 | |
214 if (statusDetail != NULL) | |
215 { | |
216 delete statusDetail; | |
217 } | |
218 } | |
219 | |
220 | |
221 static void FindCallback( | |
222 /* in */ | |
223 void *callbackData, | |
224 T_DIMSE_C_FindRQ *request, /* original find request */ | |
225 int responseCount, | |
226 T_DIMSE_C_FindRSP *response, /* pending response received */ | |
227 DcmDataset *responseIdentifiers /* pending response identifiers */ | |
228 ) | |
229 { | |
230 DicomFindAnswers& answers = *(DicomFindAnswers*) callbackData; | |
231 | |
232 if (responseIdentifiers != NULL) | |
233 { | |
234 DicomMap m; | |
235 FromDcmtkBridge::Convert(m, *responseIdentifiers); | |
236 answers.Add(m); | |
237 } | |
238 } | |
239 | |
240 void DicomUserConnection::Find(DicomFindAnswers& result, | |
241 FindRootModel model, | |
242 const DicomMap& fields) | |
243 { | |
244 CheckIsOpen(); | |
245 | |
246 const char* sopClass; | |
247 std::auto_ptr<DcmDataset> dataset(ToDcmtkBridge::Convert(fields)); | |
248 switch (model) | |
249 { | |
250 case FindRootModel_Patient: | |
251 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "PATIENT"); | |
252 sopClass = UID_FINDPatientRootQueryRetrieveInformationModel; | |
253 | |
254 // Accession number | |
255 if (!fields.HasTag(0x0008, 0x0050)) | |
256 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); | |
257 | |
258 // Patient ID | |
259 if (!fields.HasTag(0x0010, 0x0020)) | |
260 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0010, 0x0020), ""); | |
261 | |
262 break; | |
263 | |
264 case FindRootModel_Study: | |
265 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "STUDY"); | |
266 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; | |
267 | |
268 // Accession number | |
269 if (!fields.HasTag(0x0008, 0x0050)) | |
270 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); | |
271 | |
272 // Study instance UID | |
273 if (!fields.HasTag(0x0020, 0x000d)) | |
274 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); | |
275 | |
276 break; | |
277 | |
278 case FindRootModel_Series: | |
279 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "SERIES"); | |
280 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; | |
281 | |
282 // Accession number | |
283 if (!fields.HasTag(0x0008, 0x0050)) | |
284 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); | |
285 | |
286 // Study instance UID | |
287 if (!fields.HasTag(0x0020, 0x000d)) | |
288 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); | |
289 | |
290 // Series instance UID | |
291 if (!fields.HasTag(0x0020, 0x000e)) | |
292 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), ""); | |
293 | |
294 break; | |
295 | |
296 case FindRootModel_Instance: | |
519
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
297 if (manufacturer_ == ModalityManufacturer_ClearCanvas) |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
298 { |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
299 // This is a particular case for ClearCanvas, thanks to Peter Somlo <peter.somlo@gmail.com>. |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
300 // https://groups.google.com/d/msg/orthanc-users/j-6C3MAVwiw/iolB9hclom8J |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
301 // http://www.clearcanvas.ca/Home/Community/OldForums/tabid/526/aff/11/aft/14670/afv/topic/Default.aspx |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
302 printf("CLEAR CANVAS\n"); |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
303 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "IMAGE"); |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
304 } |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
305 else |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
306 { |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
307 printf("GENERIC\n"); |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
308 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "INSTANCE"); |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
309 } |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
310 |
0 | 311 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; |
312 | |
313 // Accession number | |
314 if (!fields.HasTag(0x0008, 0x0050)) | |
315 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); | |
316 | |
317 // Study instance UID | |
318 if (!fields.HasTag(0x0020, 0x000d)) | |
319 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); | |
320 | |
321 // Series instance UID | |
322 if (!fields.HasTag(0x0020, 0x000e)) | |
323 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), ""); | |
324 | |
325 // SOP Instance UID | |
326 if (!fields.HasTag(0x0008, 0x0018)) | |
327 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0018), ""); | |
328 | |
329 break; | |
330 | |
331 default: | |
62 | 332 throw OrthancException(ErrorCode_ParameterOutOfRange); |
0 | 333 } |
334 | |
335 // Figure out which of the accepted presentation contexts should be used | |
336 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); | |
337 if (presID == 0) | |
338 { | |
62 | 339 throw OrthancException("DicomUserConnection: The C-FIND command is not supported by the distant AET"); |
0 | 340 } |
341 | |
342 T_DIMSE_C_FindRQ request; | |
343 memset(&request, 0, sizeof(request)); | |
344 request.MessageID = pimpl_->assoc_->nextMsgID++; | |
345 strcpy(request.AffectedSOPClassUID, sopClass); | |
346 request.DataSetType = DIMSE_DATASET_PRESENT; | |
347 request.Priority = DIMSE_PRIORITY_MEDIUM; | |
348 | |
349 T_DIMSE_C_FindRSP response; | |
350 DcmDataset* statusDetail = NULL; | |
351 OFCondition cond = DIMSE_findUser(pimpl_->assoc_, presID, &request, dataset.get(), | |
352 FindCallback, &result, | |
353 /*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ 0, | |
354 &response, &statusDetail); | |
355 | |
356 if (statusDetail) | |
357 { | |
358 delete statusDetail; | |
359 } | |
360 | |
361 Check(cond); | |
362 } | |
363 | |
364 | |
365 void DicomUserConnection::FindPatient(DicomFindAnswers& result, | |
366 const DicomMap& fields) | |
367 { | |
368 // Only keep the filters from "fields" that are related to the patient | |
369 DicomMap s; | |
370 fields.ExtractPatientInformation(s); | |
371 Find(result, FindRootModel_Patient, s); | |
372 } | |
373 | |
374 void DicomUserConnection::FindStudy(DicomFindAnswers& result, | |
375 const DicomMap& fields) | |
376 { | |
377 // Only keep the filters from "fields" that are related to the study | |
378 DicomMap s; | |
379 fields.ExtractStudyInformation(s); | |
380 | |
80 | 381 s.CopyTagIfExists(fields, DICOM_TAG_PATIENT_ID); |
382 s.CopyTagIfExists(fields, DICOM_TAG_ACCESSION_NUMBER); | |
0 | 383 |
384 Find(result, FindRootModel_Study, s); | |
385 } | |
386 | |
387 void DicomUserConnection::FindSeries(DicomFindAnswers& result, | |
388 const DicomMap& fields) | |
389 { | |
390 // Only keep the filters from "fields" that are related to the series | |
391 DicomMap s; | |
392 fields.ExtractSeriesInformation(s); | |
393 | |
80 | 394 s.CopyTagIfExists(fields, DICOM_TAG_PATIENT_ID); |
395 s.CopyTagIfExists(fields, DICOM_TAG_ACCESSION_NUMBER); | |
396 s.CopyTagIfExists(fields, DICOM_TAG_STUDY_INSTANCE_UID); | |
0 | 397 |
398 Find(result, FindRootModel_Series, s); | |
399 } | |
400 | |
401 void DicomUserConnection::FindInstance(DicomFindAnswers& result, | |
402 const DicomMap& fields) | |
403 { | |
404 // Only keep the filters from "fields" that are related to the instance | |
405 DicomMap s; | |
406 fields.ExtractInstanceInformation(s); | |
407 | |
80 | 408 s.CopyTagIfExists(fields, DICOM_TAG_PATIENT_ID); |
409 s.CopyTagIfExists(fields, DICOM_TAG_ACCESSION_NUMBER); | |
410 s.CopyTagIfExists(fields, DICOM_TAG_STUDY_INSTANCE_UID); | |
411 s.CopyTagIfExists(fields, DICOM_TAG_SERIES_INSTANCE_UID); | |
0 | 412 |
413 Find(result, FindRootModel_Instance, s); | |
414 } | |
415 | |
416 | |
417 void DicomUserConnection::Move(const std::string& targetAet, | |
418 const DicomMap& fields) | |
419 { | |
420 CheckIsOpen(); | |
421 | |
422 const char* sopClass = UID_MOVEStudyRootQueryRetrieveInformationModel; | |
423 std::auto_ptr<DcmDataset> dataset(ToDcmtkBridge::Convert(fields)); | |
424 | |
425 // Figure out which of the accepted presentation contexts should be used | |
426 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); | |
427 if (presID == 0) | |
428 { | |
62 | 429 throw OrthancException("DicomUserConnection: The C-MOVE command is not supported by the distant AET"); |
0 | 430 } |
431 | |
432 T_DIMSE_C_MoveRQ request; | |
433 memset(&request, 0, sizeof(request)); | |
434 request.MessageID = pimpl_->assoc_->nextMsgID++; | |
435 strcpy(request.AffectedSOPClassUID, sopClass); | |
436 request.DataSetType = DIMSE_DATASET_PRESENT; | |
437 request.Priority = DIMSE_PRIORITY_MEDIUM; | |
438 strncpy(request.MoveDestination, targetAet.c_str(), sizeof(DIC_AE) / sizeof(char)); | |
439 | |
440 T_DIMSE_C_MoveRSP response; | |
441 DcmDataset* statusDetail = NULL; | |
442 DcmDataset* responseIdentifiers = NULL; | |
443 OFCondition cond = DIMSE_moveUser(pimpl_->assoc_, presID, &request, dataset.get(), | |
444 NULL, NULL, | |
445 /*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ 0, | |
446 pimpl_->net_, NULL, NULL, | |
447 &response, &statusDetail, &responseIdentifiers); | |
448 | |
449 if (statusDetail) | |
450 { | |
451 delete statusDetail; | |
452 } | |
453 | |
454 if (responseIdentifiers) | |
455 { | |
456 delete responseIdentifiers; | |
457 } | |
458 | |
459 Check(cond); | |
460 } | |
461 | |
462 | |
656 | 463 DicomUserConnection::DicomUserConnection() : |
464 pimpl_(new PImpl), | |
465 localAet_("STORESCU"), | |
466 distantAet_("ANY-SCP"), | |
467 distantHost_("127.0.0.1") | |
0 | 468 { |
469 distantPort_ = 104; | |
519
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
470 manufacturer_ = ModalityManufacturer_Generic; |
0 | 471 |
472 pimpl_->net_ = NULL; | |
473 pimpl_->params_ = NULL; | |
474 pimpl_->assoc_ = NULL; | |
475 } | |
476 | |
477 DicomUserConnection::~DicomUserConnection() | |
478 { | |
479 Close(); | |
480 } | |
481 | |
482 void DicomUserConnection::SetLocalApplicationEntityTitle(const std::string& aet) | |
483 { | |
484 Close(); | |
485 localAet_ = aet; | |
486 } | |
487 | |
488 void DicomUserConnection::SetDistantApplicationEntityTitle(const std::string& aet) | |
489 { | |
490 Close(); | |
491 distantAet_ = aet; | |
492 } | |
493 | |
519
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
494 void DicomUserConnection::SetDistantManufacturer(ModalityManufacturer manufacturer) |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
495 { |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
496 Close(); |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
497 manufacturer_ = manufacturer; |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
498 } |
1b2cdc855bd3
Parameter for PACS manufacturer, support for ClearCanvas
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
399
diff
changeset
|
499 |
0 | 500 |
501 void DicomUserConnection::SetDistantHost(const std::string& host) | |
502 { | |
503 if (host.size() > HOST_NAME_MAX - 10) | |
504 { | |
62 | 505 throw OrthancException("Distant host name is too long"); |
0 | 506 } |
507 | |
508 Close(); | |
509 distantHost_ = host; | |
510 } | |
511 | |
512 void DicomUserConnection::SetDistantPort(uint16_t port) | |
513 { | |
514 Close(); | |
515 distantPort_ = port; | |
516 } | |
517 | |
518 void DicomUserConnection::Open() | |
519 { | |
520 Close(); | |
521 | |
522 Check(ASC_initializeNetwork(NET_REQUESTOR, 0, /*opt_acse_timeout*/ 30, &pimpl_->net_)); | |
523 Check(ASC_createAssociationParameters(&pimpl_->params_, /*opt_maxReceivePDULength*/ ASC_DEFAULTMAXPDU)); | |
524 | |
525 // Set this application's title and the called application's title in the params | |
526 Check(ASC_setAPTitles(pimpl_->params_, localAet_.c_str(), distantAet_.c_str(), NULL)); | |
527 | |
528 // Set the network addresses of the local and distant entities | |
529 char localHost[HOST_NAME_MAX]; | |
530 gethostname(localHost, HOST_NAME_MAX - 1); | |
531 | |
532 char distantHostAndPort[HOST_NAME_MAX]; | |
2 | 533 |
534 #ifdef _MSC_VER | |
535 _snprintf | |
536 #else | |
537 snprintf | |
538 #endif | |
539 (distantHostAndPort, HOST_NAME_MAX - 1, "%s:%d", distantHost_.c_str(), distantPort_); | |
0 | 540 |
541 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, distantHostAndPort)); | |
542 | |
543 // Set various options | |
544 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false)); | |
545 | |
546 SetupPresentationContexts(); | |
547 | |
548 // Do the association | |
549 Check(ASC_requestAssociation(pimpl_->net_, pimpl_->params_, &pimpl_->assoc_)); | |
550 | |
551 if (ASC_countAcceptedPresentationContexts(pimpl_->params_) == 0) | |
552 { | |
62 | 553 throw OrthancException("DicomUserConnection: No Acceptable Presentation Contexts"); |
0 | 554 } |
555 } | |
556 | |
557 void DicomUserConnection::Close() | |
558 { | |
559 if (pimpl_->assoc_ != NULL) | |
560 { | |
561 ASC_releaseAssociation(pimpl_->assoc_); | |
562 ASC_destroyAssociation(&pimpl_->assoc_); | |
563 pimpl_->assoc_ = NULL; | |
564 pimpl_->params_ = NULL; | |
565 } | |
566 else | |
567 { | |
568 if (pimpl_->params_ != NULL) | |
569 { | |
570 ASC_destroyAssociationParameters(&pimpl_->params_); | |
571 pimpl_->params_ = NULL; | |
572 } | |
573 } | |
574 | |
575 if (pimpl_->net_ != NULL) | |
576 { | |
577 ASC_dropNetwork(&pimpl_->net_); | |
578 pimpl_->net_ = NULL; | |
579 } | |
580 } | |
581 | |
582 bool DicomUserConnection::IsOpen() const | |
583 { | |
584 return pimpl_->IsOpen(); | |
585 } | |
586 | |
587 void DicomUserConnection::Store(const char* buffer, size_t size) | |
588 { | |
589 // Prepare an input stream for the memory buffer | |
590 DcmInputBufferStream is; | |
591 if (size > 0) | |
592 is.setBuffer(buffer, size); | |
593 is.setEos(); | |
594 | |
595 pimpl_->Store(is); | |
596 } | |
597 | |
598 void DicomUserConnection::Store(const std::string& buffer) | |
599 { | |
600 if (buffer.size() > 0) | |
601 Store(reinterpret_cast<const char*>(&buffer[0]), buffer.size()); | |
602 else | |
603 Store(NULL, 0); | |
604 } | |
605 | |
606 void DicomUserConnection::StoreFile(const std::string& path) | |
607 { | |
608 // Prepare an input stream for the file | |
609 DcmInputFileStream is(path.c_str()); | |
610 pimpl_->Store(is); | |
611 } | |
612 | |
613 bool DicomUserConnection::Echo() | |
614 { | |
615 CheckIsOpen(); | |
616 DIC_US status; | |
617 Check(DIMSE_echoUser(pimpl_->assoc_, pimpl_->assoc_->nextMsgID++, | |
618 /*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ 0, | |
619 &status, NULL)); | |
620 return status == STATUS_Success; | |
621 } | |
622 | |
623 | |
624 void DicomUserConnection::MoveSeries(const std::string& targetAet, | |
625 const DicomMap& findResult) | |
626 { | |
627 DicomMap simplified; | |
80 | 628 simplified.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, findResult.GetValue(DICOM_TAG_STUDY_INSTANCE_UID)); |
629 simplified.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, findResult.GetValue(DICOM_TAG_SERIES_INSTANCE_UID)); | |
0 | 630 Move(targetAet, simplified); |
631 } | |
632 | |
633 void DicomUserConnection::MoveSeries(const std::string& targetAet, | |
634 const std::string& studyUid, | |
635 const std::string& seriesUid) | |
636 { | |
637 DicomMap map; | |
80 | 638 map.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid); |
639 map.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid); | |
0 | 640 Move(targetAet, map); |
641 } | |
642 | |
643 void DicomUserConnection::MoveInstance(const std::string& targetAet, | |
644 const DicomMap& findResult) | |
645 { | |
646 DicomMap simplified; | |
80 | 647 simplified.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, findResult.GetValue(DICOM_TAG_STUDY_INSTANCE_UID)); |
648 simplified.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, findResult.GetValue(DICOM_TAG_SERIES_INSTANCE_UID)); | |
649 simplified.SetValue(DICOM_TAG_SOP_INSTANCE_UID, findResult.GetValue(DICOM_TAG_SOP_INSTANCE_UID)); | |
0 | 650 Move(targetAet, simplified); |
651 } | |
652 | |
653 void DicomUserConnection::MoveInstance(const std::string& targetAet, | |
654 const std::string& studyUid, | |
655 const std::string& seriesUid, | |
656 const std::string& instanceUid) | |
657 { | |
658 DicomMap map; | |
80 | 659 map.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid); |
660 map.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid); | |
661 map.SetValue(DICOM_TAG_SOP_INSTANCE_UID, instanceUid); | |
0 | 662 Move(targetAet, map); |
663 } | |
664 | |
665 void DicomUserConnection::SetConnectionTimeout(uint32_t seconds) | |
666 { | |
667 dcmConnectionTimeout.set(seconds); | |
668 } | |
669 | |
670 } |