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