/*========================================================================= Program: GDCM (Grassroots DICOM). A DICOM library Copyright (c) 2006-2011 Mathieu Malaterre All rights reserved. See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "gdcmServiceClassUser.h" #include "gdcmDataEvent.h" #include "gdcmSimpleSubjectWatcher.h" #include "gdcmPresentationContextGenerator.h" #include "gdcmAttribute.h" #include "gdcmCompositeNetworkFunctions.h" #include "gdcmTransferSyntax.h" #include "gdcmWriter.h" #include "gdcmTesting.h" /* * This small example show how the association pipeline works. * the start association is an important step as it will define all the object * to be send in subsequence C-operation (c-echo, c-store, c-find, c-move). * In this example we will demonstrate how we can send a JPEG-Lossless object * and then further on, a non-jpeg encapsulated file * * The test also uses the Subject/Observer API for progress report. */ int TestServiceClassUser1(int argc, char *argv[]) { if( argc < 5 ) { std::cerr << argv[0] << " aetitle call portno moveReturnPort remote" << std::endl; return 1; } std::string aetitle = argv[1]; // the ae title of this computer std::string call = argv[2]; // the ae title of the server uint16_t portno = (uint16_t)atoi(argv[3]); // the port of the server uint16_t moveReturnPort = (uint16_t)atoi(argv[4]); // the port over which return cstore scps are done for cmove std::string remote = argv[5]; //the ip address of the remote server gdcm::SmartPointer scup = new gdcm::ServiceClassUser; gdcm::ServiceClassUser &scu = *scup; gdcm::SimpleSubjectWatcher w( &scu, "TestServiceClassUser1" ); scu.SetHostname( remote.c_str() ); scu.SetPort( portno ); scu.SetTimeout( 1000 ); scu.SetCalledAETitle( call.c_str() ); scu.SetAETitle( aetitle.c_str() ); std::ostringstream error_log; gdcm::Trace::SetErrorStream( error_log ); if( !scu.InitializeConnection() ) { return 1; } gdcm::PresentationContextGenerator generator; if( !generator.GenerateFromUID( gdcm::UIDs::VerificationSOPClass ) ) { return 1; } // make sure to fail if no pres. contexts: if( scu.StartAssociation() ) { return 1; } scu.SetPresentationContexts( generator.GetPresentationContexts() ); if( !scu.StartAssociation() ) { std::cerr << "Could not StartAssociation" << std::endl; std::cerr << "Error log is:" << std::endl; std::cerr << error_log.str() << std::endl; return 1; } // C-ECHO if( !scu.SendEcho() ) { std::cerr << "Could not Echo" << std::endl; std::cerr << "Error log is:" << std::endl; std::cerr << error_log.str() << std::endl; return 1; } if( !scu.StopAssociation() ) { return 1; } gdcm::Directory::FilenamesType filenames; const char *directory = gdcm::Testing::GetDataRoot(); // DEBUG: // storescu -R -xs --call GDCM_STORE macminig4 11112 gdcmData/012345.002.050.dcm std::string filename = std::string(directory) + "/012345.002.050.dcm"; filenames.push_back( filename ); if( !generator.GenerateFromFilenames(filenames) ) { return 1; } scu.SetPresentationContexts( generator.GetPresentationContexts() ); if( !scu.StartAssociation() ) { return 1; } // C-STORE MRImageStorage/JPEGLossless if( !scu.SendStore( filename.c_str() ) ) { std::cerr << "Could not C-Store" << std::endl; std::cerr << "Error log is:" << std::endl; std::cerr << error_log.str() << std::endl; return 1; } if( !scu.StopAssociation() ) { return 1; } //filename = std::string(directory) + "/MR-MONO2-12-an2.acr"; filename = std::string(directory) + "/MR_Spectroscopy_SIEMENS_OF.dcm"; filenames.clear(); filenames.push_back( filename ); if( !generator.GenerateFromFilenames(filenames) ) { return 1; } scu.SetPresentationContexts( generator.GetPresentationContexts() ); if( !scu.StartAssociation() ) { return 1; } // C-STORE MRImageStorage/LittleEndianImplicit if( !scu.SendStore( filename.c_str() ) ) { std::cerr << "Could not SendStore" << std::endl; std::cerr << "Error log is:" << std::endl; std::cerr << error_log.str() << std::endl; return 1; } if( !scu.StopAssociation() ) { return 1; } // customize the find query gdcm::DataSet findds; gdcm::Attribute<0x10,0x10> pn1 ={"ABCDEFGH^IJKLM"}; findds.Insert( pn1.GetAsDataElement() ); gdcm::Attribute<0x10,0x20> pid; findds.Insert( pid.GetAsDataElement() ); gdcm::SmartPointer findquery = gdcm::CompositeNetworkFunctions::ConstructQuery( gdcm::ePatientRootType, gdcm::ePatient, findds); // make sure the query is valid if (!findquery->ValidateQuery()) { return 1; } // Generate the PresentationContext array from the query UID: if( !generator.GenerateFromUID( findquery->GetAbstractSyntaxUID() ) ) { return 1; } scu.SetPresentationContexts( generator.GetPresentationContexts() ); if( !scu.StartAssociation() ) { return 1; } // C-FIND std::vector datasets; if( !scu.SendFind(findquery, datasets) ) { return 1; } // Need to make sure we have one dataset if( datasets.size() != 1 ) { std::cerr << "size: " << datasets.size() << std::endl; return 1; } datasets[0].Print( std::cout ); // C-find the second patient gdcm::Attribute<0x10,0x10> pn2 ={"XXXXXXXXXXX"}; findquery->GetQueryDataSet().Replace( pn2.GetAsDataElement() ); if( !scu.SendFind(findquery, datasets) ) { return 1; } if( datasets.size() != 2 ) { std::cerr << "size: " << datasets.size() << std::endl; return 1; } std::cout << std::endl; datasets[1].Print( std::cout ); if( !scu.StopAssociation() ) { return 1; } // C-MOVE // customize the move query gdcm::DataSet moveds; // use results from the c-find to construct the c-move query: moveds.Insert( datasets[0].GetDataElement( pid.GetTag() ) ); gdcm::SmartPointer movequery = gdcm::CompositeNetworkFunctions::ConstructQuery( gdcm::ePatientRootType, gdcm::ePatient, moveds, gdcm::eMove); // make sure the query is valid if (!movequery->ValidateQuery()) { return 1; } //generator.SetDefaultTransferSyntax( gdcm::TransferSyntax::JPEGLosslessProcess14_1 ); // Generate the PresentationContext array from the query UID: if( !generator.GenerateFromUID( movequery->GetAbstractSyntaxUID() ) ) { return 1; } scu.SetPresentationContexts( generator.GetPresentationContexts() ); scu.SetPortSCP( moveReturnPort ); if( !scu.StartAssociation() ) { return 1; } // C-MOVE std::vector data; if( !scu.SendMove(movequery, data) ) { return 1; } if( data.size() != 1 ) { std::cerr << "data size: " << data.size() << std::endl; return 1; } // SendMove + dataset is implicit by default: gdcm::Writer writer; writer.GetFile().GetHeader().SetDataSetTransferSyntax( gdcm::TransferSyntax::ImplicitVRLittleEndian ); writer.GetFile().SetDataSet( data[0] ); const char *outfilename = "dummy.dcm"; writer.SetFileName( outfilename ); if( !writer.Write() ) { return 1; } if( !gdcm::System::FileExists(outfilename) ) { std::cerr << "FileExists: " << outfilename << std::endl; return 1; } char digest_str[33]; if( !gdcm::Testing::ComputeFileMD5(outfilename, digest_str) ) { return 1; } if( strcmp( digest_str, "ae1f9a1bfc617f73ae8f72f81777dc03") != 0 ) { std::cerr << "md5: " << digest_str << std::endl; return 1; } // TODO: testing of CMove + JPEG Lossless is a *lot* more difficult // since we have to assume some behavior on the remote side (SCP) which // we cannot query. if( !scu.StopAssociation() ) { return 1; } // scu dstor will close the connection (!= association) return 0; }