#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 window
L_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 Level
SetElement(&responseDS, TAG_QUERY_RETRIEVE_LEVEL, VR_CS, TEXT("STUDY"));
// The Unique Keys
pElement = 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);
}
// Client
L_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 timeout
L_BOOL MessageLoop (
HANDLE hEvent, // handles that need to be waited on
DWORD 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;
// Server
L_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 hPDU
L_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-RSP
LDicomDS 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-RSP
LDicomDS 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-RSP
LogMessage(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 Request
LDicomAssociate 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 message
LogMessage(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 Query
LDicomDS 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;
}
}