LDicomNet::OnBeforeSendCommandSet (original) (raw)

Notifies a connection before the Command Set portion of a DIMSE LDicomNet::SendXxxResponse is transmitted.

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:

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; } } //