#include "ltdic.h"
L_INT LDicomNet::SendCFindRequest(nPresentationID, nMessageID, pszClass, nPriority, pDS)
L_UCHAR nPresentationID; |
presentation ID |
L_UINT16 nMessageID; |
message ID |
L_TCHAR * pszClass; |
class type |
L_UINT16 nPriority; |
priority of the message |
LDicomDS * pDS; |
data set to be found |
Sends a C-FIND-REQ message to a peer member of a connection. This function is available in the PACS Imaging Toolkit.
| Parameter | Description | |
| nPresentationID | Presentation ID. The presentation ID provides information about both the class type of the data and the transfer syntax to use when transferring the data. | |
| nMessageID | Message ID. Each message sent by a member of a connection should have a unique ID. Since a member of a connection may send several messages, this ID allows that member to identify when a specific request has been completed. | |
| pszClass | Class affected by the request. This will be an SOP Class or an SOP MetaClass. | |
| nPriority | The priority level of the message. The Service Class Provider may or may not support priority. Therefore, setting this parameter may or may not have any effect. Possible values are: | |
| Value | Meaning | |
| COMMAND_PRIORITY_LOW | [0x0002] Low priority message. | |
| COMMAND_PRIORITY_MEDIUM | [0x0000] Medium priority message. | |
| COMMAND_PRIORITY_HIGH | [0x0001] High priority message. | |
| pDS | Pointer to the data set to be found. | |
0 |
SUCCESS |
>0 |
An error occurred. Refer to Return Codes. |
Calling this function generates a call to LDicomNet::OnReceiveCFindRequest on the SCP. The SCP should respond by calling LDicomNet::SendCFindResponse which will generate a call to LDicomNet::OnReceiveCFindResponse.
You must create a data set and insert elements corresponding to the data you wish to find. A pointer to this data set is then passed as a parameter to this function.
Required DLLs and Libraries
LTDIC For a listing of the exact DLLs and Libraries needed, based on the toolkit version, refer to Files To Be Included With Your Application |
Win32, x64
Functions: |
LDicomNet::SendCFindResponse, LDicomNet::OnReceiveCFindRequest, LDicomNet::OnReceiveCFindResponse |
Topics: |
This is a basic, but complete example that shows a DICOM client sending a C-Find-REQ to a server, and the server sending three C-Find-RSP to the client.
namespace LDicomNet_SendCFindRequest_Namespace{// Logs a message// This implementation logs to the console, and the debug windowL_VOID LogMessage(TCHAR *szMsg){wprintf(TEXT("\n"));wprintf(szMsg);OutputDebugStringW(TEXT("\n"));OutputDebugStringW(szMsg);}L_VOID LogMessage(TCHAR *s, L_INT n){TCHAR szLog[200] = {0};wsprintf(szLog, TEXT("%s [%d]"), s, n);LogMessage(szLog);}L_VOID LogMessage(TCHAR *s, TCHAR *s2){TCHAR szLog[200] = {0};wsprintf(szLog, TEXT("%s [%s]"), s, s2);LogMessage(szLog);}L_VOID InsertKeyElementAndSetValue(LDicomDS& responseDS, LDicomDS& requestDS, L_UINT32 uTag, L_UINT16 uVR, L_TCHAR *pszValue){pDICOMELEMENT pElement = requestDS.FindFirstElement(NULL, uTag, TRUE);pDICOMELEMENT pNew = NULL;if (pElement){pNew = responseDS.InsertElement(NULL, FALSE, uTag, uVR, FALSE, 0);}if (pNew && pszValue){responseDS.SetConvertValue(pNew, pszValue, 1);}}L_VOID SetElement(LDicomDS* pDataSet, L_UINT32 uTag, L_UINT16 uVR, L_TCHAR* pszValue){if (pDataSet == NULL)return;pDICOMELEMENT pElement = pDataSet->FindFirstElement(NULL, uTag, TRUE);if (pElement == NULL){pElement = pDataSet->InsertElement(NULL, FALSE, uTag, uVR, FALSE, 0);}if (pElement && pszValue){pDataSet->SetConvertValue(pElement, pszValue, 1);}}L_VOID PrepareCFindResponseDataset(LDicomDS& responseDS, LDicomDS& requestDS, L_TCHAR *pszStudyInstanceUid){pDICOMELEMENT pElement = NULL;responseDS.InitDS(CLASS_UNKNOWN,DS_METAHEADER_ABSENT | DS_LITTLE_ENDIAN | DS_EXPLICIT_VR);// The Query/Retrieve LevelSetElement(&responseDS, TAG_QUERY_RETRIEVE_LEVEL, VR_CS, TEXT("STUDY"));// The Unique KeyspElement = responseDS.InsertElement(NULL, FALSE, TAG_STUDY_INSTANCE_UID, VR_UI, FALSE, 0);responseDS.SetStringValue(pElement, pszStudyInstanceUid, 1);// Required Keys:InsertKeyElementAndSetValue(responseDS, requestDS, TAG_STUDY_DATE, VR_DA, TEXT("01/02/1999"));InsertKeyElementAndSetValue(responseDS, requestDS, TAG_STUDY_TIME, VR_TM, TEXT("05:43:00"));InsertKeyElementAndSetValue(responseDS, requestDS, TAG_ACCESSION_NUMBER, VR_SH, TEXT("001"));InsertKeyElementAndSetValue(responseDS, requestDS, TAG_STUDY_ID, VR_SH, TEXT("111"));InsertKeyElementAndSetValue(responseDS, requestDS, TAG_PATIENT_NAME, VR_PN, TEXT("SMITH^JOE"));InsertKeyElementAndSetValue(responseDS, requestDS, TAG_PATIENT_ID, VR_LO, TEXT("1234"));// Optional Keys:InsertKeyElementAndSetValue(responseDS, requestDS, TAG_STUDY_DESCRIPTION, VR_LO, NULL);InsertKeyElementAndSetValue(responseDS, requestDS, TAG_NUMBER_OF_STUDY_RELATED_SERIES, VR_IS, NULL);InsertKeyElementAndSetValue(responseDS, requestDS, TAG_NUMBER_OF_STUDY_RELATED_INSTANCES, VR_IS, NULL);}// *******************************************************************************************// Client Class//// Class that is used to connect to the server// *******************************************************************************************class CMyClient : public LDicomNet{public:CMyClient(L_INT32 nMode): LDicomNet(NULL, nMode){m_waitEvent = CreateEvent( NULL, TRUE, TRUE, TEXT("ClientEvent"));ResetEvent(m_waitEvent);}~CMyClient(void){CloseHandle(m_waitEvent);}// ClientL_VOID OnConnect (L_INT nError);L_VOID OnReceiveAssociateAccept (LDicomAssociate *pPDU);L_VOID OnReceiveReleaseResponse ();L_VOID OnReceiveCFindResponse (L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass, L_UINT16 nStatus, LDicomDS *pDS);L_BOOL Wait(DWORD timeout = 5000);private:HANDLE m_waitEvent;};// Continues dispatching messages until hEvent is signalled, our timeout// Returns TRUE if hEvent is signalled// Returns FALSE if timeoutL_BOOL MessageLoop (HANDLE hEvent, // handles that need to be waited onDWORD timeout // timeout in milliseconds){DWORD dwStart = GetTickCount();MSG msg = {0};volatile L_BOOL bRunForever = TRUE;while (bRunForever){if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0){ResetEvent(hEvent);return TRUE;}DWORD dwCurrent = GetTickCount();if ((dwCurrent - dwStart) > timeout){return FALSE;}}return TRUE;}L_VOID CMyClient::OnConnect(L_INT nError){L_TCHAR szMsg[200] = {0};wsprintf(szMsg, TEXT("CMyClient::OnConnect: nError[%d]"), nError);LogMessage(szMsg);}L_VOID CMyClient::OnReceiveAssociateAccept (LDicomAssociate *pPDU){UNREFERENCED_PARAMETER(pPDU);LogMessage(TEXT("CMyClient::OnReceiveAssociateAccept"));SetEvent(m_waitEvent);}L_VOID CMyClient::OnReceiveCFindResponse(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass, L_UINT16 nStatus, LDicomDS *pDS){UNREFERENCED_PARAMETER(pDS);if (pszClass == NULL){pszClass = TEXT("");}LogMessage(TEXT("CMyClient::OnReceiveCFindResponse"));LogMessage(TEXT("\t nPresentationID"), nPresentationID);LogMessage(TEXT("\t nMessageID"), nMessageID);LogMessage(TEXT("\t pszClass"), pszClass);switch(nStatus){case COMMAND_STATUS_WARNING:LogMessage(TEXT("\t nStatus: COMMAND_STATUS_WARNING"));break;case COMMAND_STATUS_PENDING_WARNING:LogMessage(TEXT("\t nStatus: COMMAND_STATUS_PENDING_WARNING"));break;case COMMAND_STATUS_PENDING:LogMessage(TEXT("\t nStatus: COMMAND_STATUS_PENDING"));break;case COMMAND_STATUS_SUCCESS:LogMessage(TEXT("\t nStatus: COMMAND_STATUS_SUCCESS"));SetEvent(m_waitEvent);break;}}L_VOID CMyClient::OnReceiveReleaseResponse(){LogMessage(TEXT("CMyClient::OnReceiveReleaseResponse"));SetEvent(m_waitEvent);}L_BOOL CMyClient::Wait(DWORD timeout){L_BOOL bRet = MessageLoop(m_waitEvent, timeout);return bRet;}// *******************************************************************************************// Server Connection Class//// When a client connects, CMyServer creates a new instance of the CMyServerConnection class// and accepts the connection.// *******************************************************************************************class CMyServerConnection : public LDicomNet{public:CMyServerConnection(L_INT32 nMode): LDicomNet(NULL, nMode){m_waitEvent = CreateEvent( NULL, TRUE, TRUE, TEXT("ServerConnectionEvent"));ResetEvent(m_waitEvent);}~CMyServerConnection(void){CloseHandle(m_waitEvent);}private:HANDLE m_waitEvent;// ServerL_VOID OnReceiveAssociateRequest(LDicomAssociate *pPDU);L_VOID OnReceiveCFindRequest(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass, L_UINT16 nPriority, LDicomDS *pDS);L_VOID OnReceiveReleaseRequest();};#define SIZEINWORD(p) sizeof(p)/sizeof(L_TCHAR)L_VOID CMyServerConnection::OnReceiveAssociateRequest(LDicomAssociate *pPDU){LogMessage(TEXT("\tCMyServerConnection::OnReceiveAssociateRequest"));LDicomAssociate DicomAssociate(FALSE);L_TCHAR clientAE[20] = {0};pPDU->GetCalling(clientAE, 20);//Copy presentation objects from received//Reply that we only support the first Transfer Syntax from the received hPDUL_TCHAR szTransfer[PDU_MAX_UID_SIZE+1] = {0};L_TCHAR szAbstract[PDU_MAX_UID_SIZE+1] = {0};L_INT iPresentationCount = pPDU->GetPresentationCount();for (L_UCHAR i = 0; i<iPresentationCount; i++){L_UCHAR nId = pPDU->GetPresentation(i);pPDU->GetTransfer(nId, 0, szTransfer, PDU_MAX_UID_SIZE+1);L_UCHAR nResult = PDU_ACCEPT_RESULT_SUCCESS;pPDU->GetAbstract(nId, szAbstract, PDU_MAX_UID_SIZE+1);DicomAssociate.AddPresentation( nId, nResult, szAbstract);DicomAssociate.AddTransfer( nId, szTransfer);}LogMessage(TEXT("\tCMyServerConnection::SendAssociateAccept"));SendAssociateAccept(&DicomAssociate);}L_VOID CMyServerConnection::OnReceiveCFindRequest(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass, L_UINT16 nPriority, LDicomDS *pDS){UNREFERENCED_PARAMETER(pDS);LogMessage(TEXT("\tCMyServerConnection::OnReceiveCFindRequest"));L_UINT lPeerPort = 0;L_TCHAR szPeerAddress[200] = {0};GetPeerInfo(szPeerAddress,100, &lPeerPort);LogMessage(TEXT("\t\t Peer"), szPeerAddress);LogMessage(TEXT("\t\t nPresentationID"), nPresentationID);LogMessage(TEXT("\t\t nMessageID"), nMessageID);LogMessage(TEXT("\t\t pszClass"), pszClass);LogMessage(TEXT("\t\t nPriority"), nPriority);//For this example, assume the following two files match the find criteria// Prepare first pending C-Find-RSPLDicomDS ds1;PrepareCFindResponseDataset(ds1, *pDS, TEXT("1.1.1.1"));LogMessage(TEXT("\tCMyServerConnection::SendCFindResponse (0) -- PENDING"));SendCFindResponse (nPresentationID, nMessageID, pszClass, COMMAND_STATUS_PENDING, &ds1);// Prepare second pending C-Find-RSPLDicomDS ds2;PrepareCFindResponseDataset(ds2, *pDS, TEXT("1.1.1.2"));LogMessage(TEXT("\tCMyServerConnection::SendCFindResponse (1) -- PENDING"));SendCFindResponse (nPresentationID, nMessageID, pszClass, COMMAND_STATUS_PENDING, &ds2);// Send Final C-Find-RSPLogMessage(TEXT("\tCMyServerConnection::SendCFindResponse (2) -- SUCCESS"));SendCFindResponse(nPresentationID, nMessageID, pszClass, COMMAND_STATUS_SUCCESS, NULL);}L_VOID CMyServerConnection::OnReceiveReleaseRequest(){LogMessage(TEXT("\tCMyServerConnection::OnReceiveReleaseRequest"));LogMessage(TEXT("\tCMyServerConnection::SendReleaseResponse"));SendReleaseResponse();}// *******************************************************************************************// Server Class//// Listens for connections// When a client connects, this class creates a CMyServerConnection and accepts the connection// *******************************************************************************************class CMyServer : public LDicomNet{public:CMyServer(L_INT32 nMode): LDicomNet(NULL, nMode){m_pServerConnection = NULL;}~CMyServer(void){if (m_pServerConnection != NULL){delete m_pServerConnection;}}L_VOID OnAccept (L_INT nError);L_VOID OnClose (L_INT nError, LDicomNet *pServerConnection);CMyServerConnection *m_pServerConnection;};L_VOID CMyServer::OnAccept(L_INT nError){LogMessage(TEXT("\tCMyServer::OnAccept"));if (nError != DICOM_SUCCESS){return;}if (m_pServerConnection != NULL){delete m_pServerConnection;m_pServerConnection = NULL;}m_pServerConnection = new CMyServerConnection(DICOM_SECURE_NONE);if (m_pServerConnection == NULL){return;}// m_pServerConnection->EnableOptimizedSend(TRUE);nError = LDicomNet::Accept(m_pServerConnection);if (nError != DICOM_SUCCESS){delete m_pServerConnection;return;}}L_VOID CMyServer::OnClose(L_INT nError, LDicomNet *pServerConnection){UNREFERENCED_PARAMETER(nError);LogMessage(TEXT("\tCMyServer::OnClose"));if (m_pServerConnection == pServerConnection){m_pServerConnection = NULL;}delete (CMyServerConnection *)pServerConnection;}// *******************************************************************************************// Sample starts here// *******************************************************************************************#define WaitForProcessing() \{ \if (!client.Wait()) \{ \LogMessage(TEXT("Timeout: client.Connect")); \nRet = DICOM_ERROR_NET_TIME_OUT; \goto Cleanup; \} \}L_INT LDicomNet_SendCFindRequestExample(){LogMessage(TEXT("\n\n *** SendCFindRequestExample ***"));L_TCHAR *pszServerAddress = TEXT("127.0.0.1");L_UINT uServerPort = 105;L_INT nRet = DICOM_SUCCESS;LDicomNet::StartUp();CMyClient client(DICOM_SECURE_NONE);CMyServer server(DICOM_SECURE_NONE);LogMessage(TEXT("\tCMyServer::Listen"));nRet = server.Listen(pszServerAddress, uServerPort, 5);LogMessage(TEXT("CMyClient::Connect"));client.Connect(NULL, 0, pszServerAddress, uServerPort);if (!client.Wait(2000)){if (!client.IsConnected()){LogMessage(TEXT("Timeout: client.Connect"));nRet = DICOM_ERROR_NET_TIME_OUT;goto Cleanup;}}if (nRet == DICOM_SUCCESS){//create the Associate Class as RequestLDicomAssociate dicomAssociateRequest(TRUE);dicomAssociateRequest.SetCalled(TEXT("L19_PACS_SCP32"));dicomAssociateRequest.SetCalling(TEXT("LEAD_CLIENT"));dicomAssociateRequest.SetImplementClass(TRUE, TEXT("1.2.840.114257.1"));dicomAssociateRequest.SetImplementVersion(TRUE, TEXT("1"));dicomAssociateRequest.SetMaxLength(TRUE, 0x100000);dicomAssociateRequest.AddPresentation(1, 0, UID_VERIFICATION_CLASS);dicomAssociateRequest.AddTransfer(1, UID_IMPLICIT_VR_LITTLE_ENDIAN);dicomAssociateRequest.AddPresentation(3, 0, UID_STUDY_ROOT_QUERY_FIND);dicomAssociateRequest.AddTransfer(3, UID_IMPLICIT_VR_LITTLE_ENDIAN);// Send A-Associate-RQ messageLogMessage(TEXT("CMyClient::SendAssociateRequest"));nRet = client.SendAssociateRequest(&dicomAssociateRequest);if (!client.Wait(5000)){LogMessage(TEXT("Timeout: client.Connect"));nRet = DICOM_ERROR_NET_TIME_OUT;goto Cleanup;}}if (nRet == DICOM_SUCCESS){L_UCHAR nPresentationID = client.GetAssociate()->FindAbstract(UID_STUDY_ROOT_QUERY_FIND);L_UINT16 uUniqueID = 99;// Create the find dataset for a Study level, study-root QueryLDicomDS ds;ds.InitDS(CLASS_STUDY_ROOT_QUERY_STUDY, DS_ADD_MANDATORY_ELEMENTS_ONLY | DS_ADD_MANDATORY_MODULES_ONLY);pDICOMELEMENT pElement = ds.FindFirstElement(NULL, TAG_QUERY_RETRIEVE_LEVEL, TRUE);assert(pElement != NULL);ds.SetStringValue(pElement, TEXT("STUDY"),1, DICOM_CHARACTER_SET_DEFAULT);LogMessage(TEXT("CMyClient::SendCFindRequest"));L_UINT uRet = client.SendCFindRequest(nPresentationID, uUniqueID, UID_STUDY_ROOT_QUERY_FIND, COMMAND_PRIORITY_MEDIUM, &ds);UNREFERENCED_PARAMETER(uRet);WaitForProcessing();// client.Wait(10000);}LogMessage(TEXT("CMyClient::SendReleaseRequest"));client.SendReleaseRequest();WaitForProcessing();Cleanup:LogMessage(TEXT("CMyClient::Close"));client.Close();client.Wait(1000);LogMessage(TEXT("\tCMyServer::Close"));server.Close();LDicomNet::ShutDown();return nRet;}}