comparison Core/DicomNetworking/DicomStoreUserConnection.cpp @ 3831:83ea6939293d

starting DicomStoreUserConnection::Store()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 10 Apr 2020 17:56:12 +0200
parents e82bd07c384e
children 594263db316a
comparison
equal deleted inserted replaced
3830:447880856ce8 3831:83ea6939293d
32 32
33 33
34 #include "../PrecompiledHeaders.h" 34 #include "../PrecompiledHeaders.h"
35 #include "DicomStoreUserConnection.h" 35 #include "DicomStoreUserConnection.h"
36 36
37 #include "DicomAssociation.h" 37 #include "../DicomParsing/FromDcmtkBridge.h"
38 38 #include "../DicomParsing/ParsedDicomFile.h"
39 #include "../Logging.h" 39 #include "../Logging.h"
40 #include "../OrthancException.h" 40 #include "../OrthancException.h"
41 #include "DicomAssociation.h"
42
43 #include <dcmtk/dcmdata/dcdeftag.h>
44
41 45
42 namespace Orthanc 46 namespace Orthanc
43 { 47 {
44 bool DicomStoreUserConnection::ProposeStorageClass(const std::string& sopClassUid, 48 bool DicomStoreUserConnection::ProposeStorageClass(const std::string& sopClassUid,
45 const std::set<DicomTransferSyntax>& syntaxes) 49 const std::set<DicomTransferSyntax>& syntaxes)
235 **/ 239 **/
236 240
237 association_->Open(parameters_); 241 association_->Open(parameters_);
238 return LookupPresentationContext(presentationContextId, sopClassUid, transferSyntax); 242 return LookupPresentationContext(presentationContextId, sopClassUid, transferSyntax);
239 } 243 }
244
245
246 void DicomStoreUserConnection::Store(std::string& sopClassUid,
247 std::string& sopInstanceUid,
248 DcmDataset& dataset,
249 const std::string& moveOriginatorAET,
250 uint16_t moveOriginatorID)
251 {
252 OFString a, b;
253 if (!dataset.findAndGetOFString(DCM_SOPClassUID, a).good() ||
254 !dataset.findAndGetOFString(DCM_SOPInstanceUID, b).good())
255 {
256 throw OrthancException(ErrorCode_NoSopClassOrInstance,
257 "Unable to determine the SOP class/instance for C-STORE with AET " +
258 parameters_.GetRemoteApplicationEntityTitle());
259 }
260
261 sopClassUid.assign(a.c_str());
262 sopInstanceUid.assign(b.c_str());
263
264 DicomTransferSyntax transferSyntax;
265 if (!FromDcmtkBridge::LookupOrthancTransferSyntax(
266 transferSyntax, dataset.getOriginalXfer()))
267 {
268 throw OrthancException(ErrorCode_InternalError,
269 "Unknown transfer syntax from DCMTK");
270 }
271
272 // Figure out which accepted presentation context should be used
273 uint8_t presID;
274 if (!NegotiatePresentationContext(presID, sopClassUid.c_str(), transferSyntax))
275 {
276 throw OrthancException(ErrorCode_InternalError,
277 "No valid presentation context was negotiated upfront");
278 }
279
280 // Prepare the transmission of data
281 T_DIMSE_C_StoreRQ request;
282 memset(&request, 0, sizeof(request));
283 request.MessageID = association_->GetDcmtkAssociation().nextMsgID++;
284 strncpy(request.AffectedSOPClassUID, sopClassUid.c_str(), DIC_UI_LEN);
285 request.Priority = DIMSE_PRIORITY_MEDIUM;
286 request.DataSetType = DIMSE_DATASET_PRESENT;
287 strncpy(request.AffectedSOPInstanceUID, sopInstanceUid.c_str(), DIC_UI_LEN);
288
289 if (!moveOriginatorAET.empty())
290 {
291 strncpy(request.MoveOriginatorApplicationEntityTitle,
292 moveOriginatorAET.c_str(), DIC_AE_LEN);
293 request.opts = O_STORE_MOVEORIGINATORAETITLE;
294
295 request.MoveOriginatorID = moveOriginatorID; // The type DIC_US is an alias for uint16_t
296 request.opts |= O_STORE_MOVEORIGINATORID;
297 }
298
299 // Finally conduct transmission of data
300 T_DIMSE_C_StoreRSP response;
301 DcmDataset* statusDetail = NULL;
302 DicomAssociation::CheckCondition(
303 DIMSE_storeUser(&association_->GetDcmtkAssociation(), presID, &request,
304 NULL, &dataset, /*progressCallback*/ NULL, NULL,
305 /*opt_blockMode*/ (GetParameters().HasTimeout() ? DIMSE_NONBLOCKING : DIMSE_BLOCKING),
306 /*opt_dimse_timeout*/ GetParameters().GetTimeout(),
307 &response, &statusDetail, NULL),
308 GetParameters(), "C-STORE");
309
310 if (statusDetail != NULL)
311 {
312 delete statusDetail;
313 }
314
315 /**
316 * New in Orthanc 1.6.0: Deal with failures during C-STORE.
317 * http://dicom.nema.org/medical/dicom/current/output/chtml/part04/sect_B.2.3.html#table_B.2-1
318 **/
319
320 if (response.DimseStatus != 0x0000 && // Success
321 response.DimseStatus != 0xB000 && // Warning - Coercion of Data Elements
322 response.DimseStatus != 0xB007 && // Warning - Data Set does not match SOP Class
323 response.DimseStatus != 0xB006) // Warning - Elements Discarded
324 {
325 char buf[16];
326 sprintf(buf, "%04X", response.DimseStatus);
327 throw OrthancException(ErrorCode_NetworkProtocol,
328 "C-STORE SCU to AET \"" +
329 GetParameters().GetRemoteApplicationEntityTitle() +
330 "\" has failed with DIMSE status 0x" + buf);
331 }
332 }
333
334
335 void DicomStoreUserConnection::Store(std::string& sopClassUid,
336 std::string& sopInstanceUid,
337 ParsedDicomFile& parsed,
338 const std::string& moveOriginatorAET,
339 uint16_t moveOriginatorID)
340 {
341 Store(sopClassUid, sopInstanceUid, *parsed.GetDcmtkObject().getDataset(),
342 moveOriginatorAET, moveOriginatorID);
343 }
344
345
346 void DicomStoreUserConnection::Store(std::string& sopClassUid,
347 std::string& sopInstanceUid,
348 const void* buffer,
349 size_t size,
350 const std::string& moveOriginatorAET,
351 uint16_t moveOriginatorID)
352 {
353 std::unique_ptr<DcmFileFormat> dicom(
354 FromDcmtkBridge::LoadFromMemoryBuffer(buffer, size));
355
356 Store(sopClassUid, sopInstanceUid, *dicom->getDataset(),
357 moveOriginatorAET, moveOriginatorID);
358 }
240 } 359 }