Notifies a connection before the Command Set portion of a DIMSE LDicomNet::SendXxxResponse is transmitted.
#include "ltdic.h"
virtual L_VOID LDicomNet::OnBeforeSendCommandSet(pCS)
A pointer to an LDicomDS containing the Command Set portion of a DIMSE SendXxxResponse message.
None.
This callback is fired for the following functions before the Command Set portion is sent:
This allows an SCP to add/remove DICOM elements to the Command Set cs prior to transmission.
This is useful for adding optional status elements for providing additional status information including:
Win32, x64
This example shows how to send/retrieve additional elements in a DICOM command set.
A C-STORE-REQ is to a SCP.
The SCP sends a C-STORE-RSP with Status of Refused: Out of Resources, and sets the optional element (0000,0902) ErrorComment
The SCU reads the optional command set element (0000,0902) ErrorComment
namespace LDicomNet_OnBeforeSendCommandSet_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);
}
// *******************************************************************************************
// 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 OnReceiveCStoreResponse(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass, L_TCHAR *pszInstance, L_UINT16 nStatus);
L_BOOL Wait(DWORD timeout = 5000);
private:
HANDLE m_waitEvent;
};
// Continues dispatching messages until hEvent is signaled, our timeout
// Returns TRUE if hEvent is signaled
// 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::OnReceiveCStoreResponse(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass, L_TCHAR *pszInstance, L_UINT16 nStatus)
{
if (pszClass == NULL)
{
pszClass = TEXT("");
}
LogMessage(TEXT("CMyClient::OnReceiveCStoreResponse"));
LogMessage(TEXT("\t nPresentationID"), nPresentationID);
LogMessage(TEXT("\t nMessageID"), nMessageID);
LogMessage(TEXT("\t pszClass"), pszClass);
LogMessage(TEXT("\t pszInstance"), pszInstance);
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;
case COMMAND_STATUS_REFUSED_OUT_OF_RESOURCES:
{
LogMessage(TEXT("\t nStatus: COMMAND_STATUS_REFUSED_OUT_OF_RESOURCES"));
// Retrieve additional extra elements to the command set
LDicomDS *pCS = GetCommandSet();
if (pCS != NULL)
{
pDICOMELEMENT pElement = pCS->FindFirstElement(NULL, TAG_ERROR_COMMENT, TRUE);
if (pElement != NULL)
{
L_TCHAR *pszErrorComment = pCS->GetStringValue(pElement, 0, 1);
LogMessage(TEXT("\t ErrorComment: "), pszErrorComment);
}
}
}
SetEvent(m_waitEvent);
break;
}
SetEvent(m_waitEvent);
}
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)
{
}
~CMyServerConnection(void)
{
}
// Server
L_VOID OnReceiveAssociateRequest(LDicomAssociate *pPDU);
L_VOID OnReceiveCStoreRequest(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass, L_TCHAR *pszInstance, L_UINT16 nPriority, L_TCHAR *pszMoveAE, L_UINT16 nMoveMessageID, LDicomDS *pDS);
L_VOID OnReceiveReleaseRequest();
L_VOID OnBeforeSendCommandSet(LDicomDS *pCS);
};
#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::OnReceiveCStoreRequest(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass, L_TCHAR *pszInstance, L_UINT16 nPriority, L_TCHAR *pszMoveAE, L_UINT16 nMoveMessageID, LDicomDS *pDS)
{
UNREFERENCED_PARAMETER(pDS);
UNREFERENCED_PARAMETER(pszMoveAE);
LogMessage(TEXT("\tCMyServerConnection::OnReceiveCStoreRequest"));
LogMessage(TEXT("\t nMoveMessageID"), nMoveMessageID);
LogMessage(TEXT("\t nPriority"), nPriority);
//...
//...Do the store here
//...nStatus = status of the store
LogMessage(TEXT("\t\t Do the store here"));
// Send C-Store-RSP
LogMessage(TEXT("\tCMyServerConnection::SendCStoreResponse"));
SendCStoreResponse(nPresentationID, nMessageID, pszClass, pszInstance, COMMAND_STATUS_SUCCESS);
}
L_VOID CMyServerConnection::OnBeforeSendCommandSet(LDicomDS *pCS)
{
// Add any extra elements to the command
// For this example, the store request will fail with a status of "Refused: Out of Resources"
// The reason is given in TAG_ERROR_COMMENT
if (pCS != NULL)
{
pDICOMELEMENT pElement = NULL;
pElement = pCS->FindFirstElement(NULL, TAG_STATUS, TRUE);
if (pElement != NULL)
{
L_UINT16 nStatus = COMMAND_STATUS_REFUSED_OUT_OF_RESOURCES;
pCS->SetShortValue(pElement, (L_INT16 *)&nStatus, 1);
}
pElement = pCS->InsertElement(NULL, FALSE, TAG_ERROR_COMMENT, VR_LO, FALSE, 0);
if (pElement != NULL)
{
L_TCHAR szErrorComment[] = TEXT("Out of disk space");
pCS->SetStringValue(pElement, szErrorComment, 1);
}
}
}
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_OnBeforeSendCommandSetExample()
{
LogMessage(TEXT("\n\n *** OnBeforeSendCommandSetExample ***"));
L_TCHAR *pszServerAddress = TEXT("127.0.0.1");
L_UINT uServerPort = 504;
L_INT nRet = DICOM_SUCCESS;
// Load the DICOM dataset that the client will store
LDicomDS ds;
// ds.LoadDS(MAKE_IMAGE_PATH(TEXT("IMAGE1.dcm")), DS_LOAD_CLOSE);
ds.LoadDS((TEXT("d:\\images\\image3.dcm")), DS_LOAD_CLOSE);
L_TCHAR *pszStorageClass = NULL;
pDICOMELEMENT pElement = ds.FindFirstElement(NULL, TAG_MEDIA_STORAGE_SOP_CLASS_UID, TRUE);
if (pElement != NULL)
{
pszStorageClass = ds.GetStringValue(pElement, 0, 1);
}
if (pszStorageClass == NULL || _tcslen(pszStorageClass) == 0)
{
pElement = ds.FindFirstElement(NULL, TAG_SOP_CLASS_UID, TRUE);
if (pElement != NULL)
{
pszStorageClass = ds.GetStringValue(pElement, 0, 1);
}
}
if (pszStorageClass == NULL || _tcslen(pszStorageClass) == 0)
{
pszStorageClass = UID_CT_IMAGE_STORAGE; // Default to CT Image Storage
}
//
// Get Image transfer syntax
//
L_TCHAR *pszTransferSyntax = NULL;
L_TCHAR *pszStorageInstance = NULL;
pElement = ds.FindFirstElement(NULL, TAG_TRANSFER_SYNTAX_UID, TRUE);
if (pElement != NULL)
{
pszTransferSyntax = ds.GetStringValue(pElement, 0, 1);
}
pElement = ds.FindFirstElement(NULL, TAG_SOP_INSTANCE_UID, TRUE);
if (pElement != NULL)
{
pszStorageInstance = ds.GetStringValue(pElement, 0, 1);
}
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("L20_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, pszStorageClass);
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(pszStorageClass);
L_UINT16 uUniqueID = 99;
LogMessage(TEXT("CMyClient::SendCStoreRequest"));
client.SendCStoreRequest(nPresentationID, uUniqueID, pszStorageClass, pszStorageInstance, COMMAND_PRIORITY_MEDIUM, TEXT("NONE"), 1, &ds);
WaitForProcessing();
}
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;
}
}
//