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 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);}// *******************************************************************************************// 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 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 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::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 setLDicomDS *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){}// ServerL_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 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::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 storeLogMessage(TEXT("\t\t Do the store here"));// Send C-Store-RSPLogMessage(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_COMMENTif (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 storeLDicomDS 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 RequestLDicomAssociate 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 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(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;}}//