// WListSCP.cpp: implementation of the LMwlScp class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "WListSCP.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif // The implementation Class #define IMPLEMENTATION_CLASS_UID "1.2.840.114257.1123456" // The implementation Version #define IMPLEMENTATION_VERSION_NAME "1" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CWorklistSCP::CWorklistSCP(L_CHAR* pszPath) : LDicomNet(pszPath, DICOM_SECURE_NONE) { m_pServerObject = this; m_pszServerName = NULL; m_pClientsInfo = NULL; m_pszWorkingDBPath = NULL; m_uClientsCount = 0; m_uNumMaxPeers= 0; } CWorklistSCP::~CWorklistSCP() { // Free any allocated memory if (m_pszServerName) { delete [] m_pszServerName; m_pszServerName = NULL; } if (m_pszWorkingDBPath) { delete [] m_pszWorkingDBPath; m_pszWorkingDBPath = NULL; } if (m_pClientsInfo) { delete [] m_pClientsInfo; m_pClientsInfo = NULL; } // Close and delete all clients connected CWorklistSCP* pClient; while (GetClientCount() > 0) { pClient = (CWorklistSCP*) GetClient(0); if (pClient) { pClient->Close(); delete pClient; } } } L_VOID CWorklistSCP::OnClientsStatus(const CString& sNewClient) { } L_VOID CWorklistSCP::OnStatus(const CString& sClientName, const CString& sStatus, L_INT nErrorCode) { if (m_pServerObject) { m_pServerObject->OnStatus(sClientName, sStatus, nErrorCode); } } L_INT CWorklistSCP::StartListening(L_UINT uPort, L_UINT uMaxClientsCount, L_CHAR* pszCalledTitle) { if (!pszCalledTitle) { return DICOM_ERROR_PARAMETER; } if (m_pszServerName) { delete [] m_pszServerName; m_pszServerName = NULL; } m_pszServerName = new L_CHAR[lstrlen(pszCalledTitle) + 1]; if (!m_pszServerName) { return DICOM_ERROR_MEMORY; } // The SCP AE name lstrcpy(m_pszServerName, pszCalledTitle); m_uNumMaxPeers = uMaxClientsCount; L_INT nRet = Listen("", uPort, uMaxClientsCount); if (nRet != DICOM_SUCCESS) { // Status OnStatus("", "Failed to start listening!", nRet); return nRet; } // Status OnStatus("", "Server is listening!"); return nRet; } L_INT CWorklistSCP::StopListening() { // Free any allocated memory if (m_pszServerName) { delete [] m_pszServerName; m_pszServerName = NULL; } // Close and delete all clients connected CWorklistSCP* pClient; while (GetClientCount() > 0) { pClient = (CWorklistSCP*) GetClient(0); if (pClient) { pClient->Close(); delete pClient; } } // Status OnStatus("", "Server is stopped!"); Close(); return DICOM_SUCCESS; } L_INT CWorklistSCP::SetWorkingDBName(L_CHAR* pszDBPath) { if (!pszDBPath) { return DICOM_ERROR_PARAMETER; } m_pszWorkingDBPath = new L_CHAR[lstrlen(pszDBPath) + 1]; if (!m_pszWorkingDBPath) { return DICOM_ERROR_MEMORY; } // The SCP AE name lstrcpy(m_pszWorkingDBPath, pszDBPath); return DICOM_SUCCESS; } L_INT CWorklistSCP::SetClientsInfo(pCLIENTINFO AllClients, L_UINT uCount) { if (m_pClientsInfo) { delete [] m_pClientsInfo; m_pClientsInfo = NULL; } m_uClientsCount = 0; if (!AllClients) { return DICOM_SUCCESS; } m_pClientsInfo = new CLIENTINFO[uCount]; if (!m_pClientsInfo) { return DICOM_ERROR_MEMORY; } L_UINT ii; // Copy the passed structure to the current structure for (ii = 0; ii < uCount; ii++) { lstrcpyn(m_pClientsInfo[ii].szClientName, AllClients[ii].szClientName, sizeof(m_pClientsInfo[ii].szClientName)); lstrcpyn(m_pClientsInfo[ii].szClientIP, AllClients[ii].szClientIP, sizeof(m_pClientsInfo[ii].szClientIP)); } m_uClientsCount = uCount; return DICOM_SUCCESS; } L_BOOL CWorklistSCP::ValidClientAETitle(L_CHAR* pszCLientName) { L_UINT Counter; // Check m_pServerObject for being NULL before proceeding if (!m_pServerObject) return FALSE; if (m_pServerObject->m_uClientsCount == 0) return TRUE; for (Counter = 0; Counter < m_pServerObject->m_uClientsCount; Counter++) { if (lstrcmp(m_pServerObject->m_pClientsInfo[Counter].szClientName, pszCLientName) == 0) { return TRUE; } } return FALSE; } L_BOOL CWorklistSCP::ValidClientIP(L_CHAR* pszCLientIP) { L_UINT Counter; if (m_uClientsCount == 0) return TRUE; for (Counter = 0; Counter < m_uClientsCount ; Counter++) { if (lstrcmp(m_pClientsInfo[Counter].szClientIP, pszCLientIP) == 0) { return TRUE; } } return FALSE; } // Callbacks L_VOID CWorklistSCP::OnAccept(L_INT nError) { L_CHAR szClientAddress[100]; L_UINT lClientPort; CString strMsg; CWorklistSCP *pDicomNetClient; L_UINT32 lClientCount; if (nError != DICOM_SUCCESS) return; // Server must accept the connection attempt -- can reject it later pDicomNetClient = new CWorklistSCP(); if (!pDicomNetClient) { // Status OnStatus("", "Failed in accept the connection.", DICOM_ERROR_MEMORY); return; } pDicomNetClient->m_pServerObject = this; Accept(pDicomNetClient); // Get total client count lClientCount = GetClientCount(); if ((m_uNumMaxPeers != 0) && (GetClientCount() > m_uNumMaxPeers)) { pDicomNetClient->GetPeerInfo(szClientAddress, &lClientPort); strMsg.Format("Attempt to connect by client:IP Address[%s],Port[%d] was refused\r\nNumber of concurrent connections exceeds maximum allowed!\r\n", szClientAddress, lClientPort); // Maximum number of connections has been reached --> Reject the connection pDicomNetClient->Close(); delete pDicomNetClient; OnStatus("", strMsg, DICOM_SUCCESS); return; } // Get client information pDicomNetClient->GetPeerInfo(szClientAddress, &lClientPort); // Check the validity of the client IP if (ValidClientIP(szClientAddress) == FALSE) { // Reject the connection pDicomNetClient->Close(); delete pDicomNetClient; OnStatus("", "Failed in accept the connection (Not valid client)."); return; } strMsg.Format("ClientAddress[%s], ClientPort[%d]\r\n", szClientAddress, lClientPort); // Status OnClientsStatus(strMsg); } L_VOID CWorklistSCP::OnReceiveAssociateRequest (LDicomAssociate *pPDU) { L_CHAR* pszCurClientName; // Get the Calling AE title pszCurClientName = pPDU->GetCalling(); // Status OnStatus(pszCurClientName, "Receiving Associate Request."); // Check the version, if not 1, reject it if (pPDU->GetVersion() != 1) { SendAssociateReject(PDU_REJECT_RESULT_PERMANENT, PDU_REJECT_SOURCE_PROVIDER1, PDU_REJECT_REASON_VERSION); // Status OnStatus(pszCurClientName, "Version Not supported.\n\r Sending associate reject!"); return; } if (ValidClientAETitle(pszCurClientName) == FALSE) { // Not supported client SendAssociateReject(PDU_REJECT_RESULT_PERMANENT, PDU_REJECT_SOURCE_USER, PDU_REJECT_REASON_CALLING); // Status OnStatus(pszCurClientName, "Failed in accept the connection (Not valid client name)!"); return; } // Check the Called AE title (server) if (lstrcmp(pPDU->GetCalled(), m_pServerObject->m_pszServerName) != 0) { SendAssociateReject(PDU_REJECT_RESULT_PERMANENT, PDU_REJECT_SOURCE_USER, PDU_REJECT_REASON_CALLED); OnStatus(pszCurClientName, "Server names are not the same.\n\r Sending associate reject!"); return; } // Send associate accept back LDicomAssociate RetDicomAssociate(FALSE); // Set the version RetDicomAssociate.SetVersion(1); // Set the AE titles RetDicomAssociate.SetCalled(m_pServerObject->m_pszServerName); RetDicomAssociate.SetCalling(pszCurClientName); // The Application Context Name RetDicomAssociate.SetApplication(UID_APPLICATION_CONTEXT_NAME); L_INT nPresCount = pPDU->GetPresentationCount(); L_UCHAR nPresContID; L_INT Counter; // Presentation Contexts L_CHAR* pszAbstractSynName; for (Counter = 0; Counter < nPresCount; Counter++) { nPresContID = pPDU->GetPresentation(Counter); pszAbstractSynName = pPDU->GetAbstract(nPresContID); RetDicomAssociate.AddPresentation(nPresContID, PDU_ACCEPT_RESULT_SUCCESS, pszAbstractSynName); L_INT nTransSynCount; L_CHAR* pszTransSynName; // If not verification nor MWL if ((lstrcmp(pszAbstractSynName, UID_VERIFICATION_CLASS) != 0) && (lstrcmp(pszAbstractSynName, UID_MODALITY_WORKLIST_FIND) != 0)) { RetDicomAssociate.SetResult(nPresContID, PDU_ACCEPT_RESULT_ABSTRACT_SYNTAX); } L_BOOL bSupported = FALSE; // Check the verification SOP class if (lstrcmp(pszAbstractSynName, UID_VERIFICATION_CLASS) == 0) { // Check the default transfer syntax nTransSynCount = pPDU->GetTransferCount(nPresContID); for (L_INT SecCount = 0; SecCount < nTransSynCount; SecCount++) { pszTransSynName = pPDU->GetTransfer(nPresContID, SecCount); // Compare with the default transfer syntax if (lstrcmp(pszTransSynName, UID_IMPLICIT_VR_LITTLE_ENDIAN) == 0) // UID_IMPLICIT_VR_LITTLE_ENDIAN is the default transfer syntax { // Adding the default transfer syntax RetDicomAssociate.AddTransfer(nPresContID, pszTransSynName); bSupported = TRUE; break; // End the loop } } if (!bSupported) { RetDicomAssociate.SetResult(nPresContID, PDU_ACCEPT_RESULT_TRANSFER_SYNTAX); } } // Check the MWL SOP class if (lstrcmp(pszAbstractSynName, UID_MODALITY_WORKLIST_FIND) == 0) { bSupported = FALSE; // Check the default transfer syntax nTransSynCount = pPDU->GetTransferCount(nPresContID); for (L_INT SecCount = 0; SecCount < nTransSynCount; SecCount++) { pszTransSynName = pPDU->GetTransfer(nPresContID, SecCount); // Compare with the default transfer syntax if (lstrcmp(pszTransSynName, UID_IMPLICIT_VR_LITTLE_ENDIAN) == 0) // UID_IMPLICIT_VR_LITTLE_ENDIAN is the default transfer syntax { // Adding the default transfer syntax RetDicomAssociate.AddTransfer(nPresContID, pszTransSynName); bSupported = TRUE; break; // End the loop } } if (!bSupported) { RetDicomAssociate.SetResult(nPresContID, PDU_ACCEPT_RESULT_TRANSFER_SYNTAX); } } } // Maximum Length Application PDU if(pPDU->IsMaxLength()) { RetDicomAssociate.SetMaxLength(TRUE, pPDU->GetMaxLength()); } // Implementation Class UID RetDicomAssociate.SetImplementClass(TRUE, IMPLEMENTATION_CLASS_UID); // Implementation Version Name RetDicomAssociate.SetImplementVersion(TRUE, IMPLEMENTATION_VERSION_NAME); // Send the response OnStatus(pszCurClientName, "Sending Associate Accept."); SendAssociateAccept(&RetDicomAssociate); } L_VOID CWorklistSCP::OnReceiveReleaseRequest() { L_CHAR* pszCurClientName; // Get the Calling AE title pszCurClientName = GetAssociate()->GetCalling(); OnStatus(pszCurClientName, "Receiving Release Request..."); // Status OnStatus(pszCurClientName, "Sending Release Response..."); // Send Release Response SendReleaseResponse(); return; } L_VOID CWorklistSCP::OnReceiveCFindRequest(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_CHAR *pszClass, L_UINT16 nPriority, LDicomDS *pDS) { LDicomAssociate* pPDU = GetAssociate(); if (!pPDU) { // The connection is not associated, so ignore the message return; } CString sCurUser; sCurUser = pPDU->GetCalling(); // We have received a C-Find Request // Status OnStatus(sCurUser, "Receiving C-Find Request..."); L_UINT16 nStatus = COMMAND_STATUS_SUCCESS; CString sStatus; pDICOMUID pUID = LDicomUID::Find(pszClass); if (!pUID) { nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; sStatus = "Sending C-Find Response - Status: Abstract Syntax not supported!"; } else { L_UCHAR nID = pPDU->FindAbstract(pszClass); if ((nID == 0) || (pPDU->GetResult(nID) != PDU_ACCEPT_RESULT_SUCCESS)) { nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; sStatus = "Sending C-Find Response - Status: Abstract Syntax not supported!"; } } if (!pDS) { nStatus = COMMAND_STATUS_INVALID_ARGUMENT_VALUE; sStatus = "Sending C-Find Response - Status: Insvalid argument value!"; } if (nStatus != COMMAND_STATUS_SUCCESS) { L_INT nRet = SendCFindResponse(nPresentationID, nMessageID, pszClass, nStatus, NULL); OnStatus(sCurUser, sStatus, nRet); return; } CString sSQLQuery; GenerateMatchingQuery(pDS, sCurUser, sSQLQuery); FindMatchingAttributes(pDS, nPresentationID, nMessageID, pszClass, sCurUser, sSQLQuery); } L_VOID CWorklistSCP::FindMatchingAttributes(LDicomDS* pReqIdentifier, L_UCHAR nPresentationID, L_UINT16 uMessageID, L_CHAR* pszClass, const CString& sUser, const CString& sSQLQuery) { // Get matched data from the DB CDatabase CurDatabase; CString sConnString; CString sFileName; L_INT nRet; sFileName = m_pServerObject->m_pszWorkingDBPath; sConnString = "DRIVER={Microsoft Access Driver (*.mdb)};" \ "DBQ=" + sFileName; try { CurDatabase.OpenEx(_T(sConnString), CDatabase::noOdbcDialog); } catch(...) { nRet = SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PROCESSING_FAILURE, NULL); OnStatus(sUser, "Sending C-Find Response - Status: Processing failure", nRet); return; } CRecordset CurRecordSet(&CurDatabase); try { CurRecordSet.Open(CRecordset::dynaset, _T(sSQLQuery)); LDicomDS ResDicomDS; PrepareResponse(ResDicomDS, *pReqIdentifier); // Create a CDBVariant object to store field data CDBVariant varValue; CString* psValue; double* pdValue; TIMESTAMP_STRUCT* pTS; pDICOMELEMENT pElement; short nFields = CurRecordSet.GetODBCFieldCount(); // Loop over the retrieved data while (!CurRecordSet.IsEOF()) { for (short index = 0; index < nFields; index++) { CurRecordSet.GetFieldValue(index, varValue); psValue = NULL; pdValue = NULL; pTS = NULL; switch (varValue.m_dwType) { case DBVT_STRING: psValue = varValue.m_pstring; break; case DBVT_DOUBLE: pdValue = &varValue.m_dblVal; break; case DBVT_DATE: pTS = varValue.m_pdate; break; } switch (index) { case 1 : //TAG_ACCESSION_NUMBER: SetKeyElement(ResDicomDS, psValue, TAG_ACCESSION_NUMBER); break; case 2: //TAG_MODALITY: SetKeyElement(ResDicomDS, psValue, TAG_MODALITY); break; case 3: //TAG_INSTITUTION_NAME: SetKeyElement(ResDicomDS, psValue, TAG_INSTITUTION_NAME); break; case 4: //TAG_REFERRING_PHYSICIAN_NAME: SetKeyElement(ResDicomDS, psValue, TAG_REFERRING_PHYSICIAN_NAME); break; case 5: // TAG_PATIENT_NAME: SetKeyElement(ResDicomDS, psValue, TAG_PATIENT_NAME); break; case 6: //TAG_PATIENT_ID: SetKeyElement(ResDicomDS, psValue, TAG_PATIENT_ID); break; case 7: //TAG_PATIENT_BIRTH_DATE: SetTimeDateKeyElement(ResDicomDS, pTS, TAG_PATIENT_BIRTH_DATE, FALSE); break; case 8: //TAG_PATIENT_SEX: SetKeyElement(ResDicomDS, psValue, TAG_PATIENT_SEX); break; case 9: //TAG_PATIENT_WEIGHT: pElement = ResDicomDS.FindFirstElement(NULL, TAG_PATIENT_WEIGHT, FALSE); if (pElement) { if (pdValue) { ResDicomDS.SetDoubleValue(pElement, pdValue, 1); } else { ResDicomDS.SetBinaryValue(pElement, NULL, 0); } } break; case 10: //TAG_STUDY_INSTANCE_UID: SetKeyElement(ResDicomDS, psValue, TAG_STUDY_INSTANCE_UID); break; case 11: //TAG_REQUESTING_PHYSICIAN: SetKeyElement(ResDicomDS, psValue, TAG_REQUESTING_PHYSICIAN); break; case 12: //TAG_REQUESTED_PROCEDURE_DESCRIPTION: SetKeyElement(ResDicomDS, psValue, TAG_REQUESTED_PROCEDURE_DESCRIPTION); break; case 13: //TAG_ADMISSION_ID: SetKeyElement(ResDicomDS, psValue, TAG_ADMISSION_ID); break; case 14: //TAG_SCHEDULED_STATION_AE_TITLE: SetKeyElement(ResDicomDS, psValue, TAG_SCHEDULED_STATION_AE_TITLE); break; case 15: //TAG_SCHEDULED_PROCEDURE_STEP_START_DATE: SetTimeDateKeyElement(ResDicomDS, pTS, TAG_SCHEDULED_PROCEDURE_STEP_START_DATE, FALSE); break; case 16: //TAG_SCHEDULED_PROCEDURE_STEP_START_TIME: SetTimeDateKeyElement(ResDicomDS, pTS, TAG_SCHEDULED_PROCEDURE_STEP_START_TIME, TRUE); break; case 17: //TAG_SCHEDULED_PERFORMING_PHYSICIAN_NAME: SetKeyElement(ResDicomDS, psValue, TAG_SCHEDULED_PERFORMING_PHYSICIAN_NAME); break; case 18: //TAG_SCHEDULED_PROCEDURE_STEP_DESCRIPTION: SetKeyElement(ResDicomDS, psValue, TAG_SCHEDULED_PROCEDURE_STEP_DESCRIPTION); break; case 19: //TAG_SCHEDULED_PROCEDURE_STEP_ID: SetKeyElement(ResDicomDS, psValue, TAG_SCHEDULED_PROCEDURE_STEP_ID); break; case 20: //TAG_SCHEDULED_PROCEDURE_STEP_LOCATION: SetKeyElement(ResDicomDS, psValue, TAG_SCHEDULED_PROCEDURE_STEP_LOCATION); break; case 21: //TAG_REQUESTED_PROCEDURE_ID: SetKeyElement(ResDicomDS, psValue, TAG_REQUESTED_PROCEDURE_ID); break; case 22: //TAG_REASON_FOR_THE_REQUESTED_PROCEDURE: SetKeyElement(ResDicomDS, psValue, TAG_REASON_FOR_THE_REQUESTED_PROCEDURE); break; case 23: //TAG_REQUESTED_PROCEDURE_PRIORITY: SetKeyElement(ResDicomDS, psValue, TAG_REQUESTED_PROCEDURE_PRIORITY); break; } } // Sending the C-Find response for this match nRet = SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PENDING, &ResDicomDS); OnStatus(sUser, "Sending C-Find Response - Status: Pending", nRet); CurRecordSet.MoveNext(); } CurRecordSet.Close(); } catch(...) { nRet = SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PROCESSING_FAILURE, NULL); OnStatus(sUser, "Sending C-Find Response - Status: Processing failure", nRet); return; } CurDatabase.Close(); // The final C-FIND-RSP nRet = SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_SUCCESS, NULL); OnStatus(sUser, "Sending C-Find Response - Status: Success", nRet); } L_VOID CWorklistSCP::PrepareResponse(LDicomDS& ResDicomDS, LDicomDS& ReqDicomDS) { ResDicomDS.InitDS(CLASS_UNKNOWN, DS_LITTLE_ENDIAN | DS_IMPLICIT_VR); pDICOMELEMENT pElement; // Scheduled Procedure Step pElement = ReqDicomDS.FindFirstElement(NULL, TAG_SCHEDULED_PROCEDURE_STEP_SEQUENCE, FALSE); if (pElement) { pDICOMELEMENT pSequence, pItem = NULL; pSequence = ResDicomDS.InsertElement(NULL, FALSE, TAG_SCHEDULED_PROCEDURE_STEP_SEQUENCE, VR_SQ, TRUE, 0); if (pSequence) { pItem = ResDicomDS.InsertElement(pSequence, TRUE, TAG_ITEM, VR_SQ, FALSE, 0); } if (pItem) { pElement = ReqDicomDS.GetChildElement(pElement, TRUE); if (pElement) { pElement = ReqDicomDS.GetChildElement(pElement, TRUE); while (pElement) { switch (pElement->nTag) { case TAG_SCHEDULED_STATION_AE_TITLE: case TAG_SCHEDULED_PROCEDURE_STEP_START_DATE: case TAG_SCHEDULED_PROCEDURE_STEP_START_TIME: case TAG_MODALITY: case TAG_SCHEDULED_PERFORMING_PHYSICIAN_NAME: case TAG_SCHEDULED_PROCEDURE_STEP_DESCRIPTION: case TAG_SCHEDULED_PROCEDURE_STEP_LOCATION: case TAG_SCHEDULED_PROCEDURE_STEP_ID: ResDicomDS.InsertElement(pItem, TRUE, pElement->nTag, 0, FALSE, 0); break; } pElement = ReqDicomDS.GetNextElement(pElement, TRUE, TRUE); } } else { ResDicomDS.InsertElement(pItem, TRUE, TAG_SCHEDULED_STATION_AE_TITLE, 0, FALSE, 0); ResDicomDS.InsertElement(pItem, TRUE, TAG_SCHEDULED_PROCEDURE_STEP_START_DATE, 0, FALSE, 0); ResDicomDS.InsertElement(pItem, TRUE, TAG_SCHEDULED_PROCEDURE_STEP_START_TIME, 0, FALSE, 0); ResDicomDS.InsertElement(pItem, TRUE, TAG_MODALITY, 0, FALSE, 0); ResDicomDS.InsertElement(pItem, TRUE, TAG_SCHEDULED_PERFORMING_PHYSICIAN_NAME, 0, FALSE, 0); ResDicomDS.InsertElement(pItem, TRUE, TAG_SCHEDULED_PROCEDURE_STEP_DESCRIPTION, 0, FALSE, 0); ResDicomDS.InsertElement(pItem, TRUE, TAG_SCHEDULED_PROCEDURE_STEP_LOCATION, 0, FALSE, 0); ResDicomDS.InsertElement(pItem, TRUE, TAG_SCHEDULED_PROCEDURE_STEP_ID, 0, FALSE, 0); } } } // Requested Procedure if (ReqDicomDS.FindFirstElement(NULL, TAG_REQUESTED_PROCEDURE_ID, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_REQUESTED_PROCEDURE_ID, 0, FALSE, 0); } if (ReqDicomDS.FindFirstElement(NULL, TAG_REQUESTED_PROCEDURE_DESCRIPTION, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_REQUESTED_PROCEDURE_DESCRIPTION, 0, FALSE, 0); } if (ReqDicomDS.FindFirstElement(NULL, TAG_STUDY_INSTANCE_UID, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_STUDY_INSTANCE_UID, 0, FALSE, 0); } if (ReqDicomDS.FindFirstElement(NULL, TAG_REQUESTED_PROCEDURE_PRIORITY, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_REQUESTED_PROCEDURE_PRIORITY, 0, FALSE, 0); } if (ReqDicomDS.FindFirstElement(NULL, TAG_REASON_FOR_THE_REQUESTED_PROCEDURE, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_REASON_FOR_THE_REQUESTED_PROCEDURE, 0, FALSE, 0); } // Imaging Service Request if (ReqDicomDS.FindFirstElement(NULL, TAG_ACCESSION_NUMBER, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_ACCESSION_NUMBER, 0, FALSE, 0); } if (ReqDicomDS.FindFirstElement(NULL, TAG_REQUESTING_PHYSICIAN, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_REQUESTING_PHYSICIAN, 0, FALSE, 0); } if (ReqDicomDS.FindFirstElement(NULL, TAG_REFERRING_PHYSICIAN_NAME, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_REFERRING_PHYSICIAN_NAME, 0, FALSE, 0); } // Visit Identification if (ReqDicomDS.FindFirstElement(NULL, TAG_ADMISSION_ID, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_ADMISSION_ID, 0, FALSE, 0); } if (ReqDicomDS.FindFirstElement(NULL, TAG_INSTITUTION_NAME, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_INSTITUTION_NAME, 0, FALSE, 0); } // Patient Identification if (ReqDicomDS.FindFirstElement(NULL, TAG_PATIENT_NAME, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_PATIENT_NAME, 0, FALSE, 0); } if (ReqDicomDS.FindFirstElement(NULL, TAG_PATIENT_ID, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_PATIENT_ID, 0, FALSE, 0); } // Patient Demographic if (ReqDicomDS.FindFirstElement(NULL, TAG_PATIENT_BIRTH_DATE, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_PATIENT_BIRTH_DATE, 0, FALSE, 0); } if (ReqDicomDS.FindFirstElement(NULL, TAG_PATIENT_SEX, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_PATIENT_SEX, 0, FALSE, 0); } if (ReqDicomDS.FindFirstElement(NULL, TAG_PATIENT_WEIGHT, FALSE)) { ResDicomDS.InsertElement(NULL, FALSE, TAG_PATIENT_WEIGHT, 0, FALSE, 0); } } L_VOID CWorklistSCP::SetKeyElement(LDicomDS& RspIdentifier, const CString* psValue, L_UINT32 uTag) const { pDICOMELEMENT pElement = RspIdentifier.FindFirstElement(NULL, uTag, FALSE); if (pElement) { if (psValue) { RspIdentifier.SetConvertValue(pElement, (L_CHAR*)(LPCTSTR) *psValue, 1); } else { RspIdentifier.SetBinaryValue(pElement, NULL, 0); } } } L_VOID CWorklistSCP::SetTimeDateKeyElement(LDicomDS& RspIdentifier, TIMESTAMP_STRUCT* pTS, L_UINT32 uTag, L_BOOL bTimeValue) const { pDICOMELEMENT pElement = RspIdentifier.FindFirstElement(NULL, uTag, FALSE); if (pElement) { if (!pTS) { RspIdentifier.SetBinaryValue(pElement, NULL, 0); return; } if (bTimeValue) { VALUETIME vt; vt.nHours = pTS->hour; vt.nMinutes = pTS->minute; vt.nSeconds = pTS->second; vt.nFractions = pTS->fraction; RspIdentifier.SetTimeValue(pElement, &vt, 1); } else { VALUEDATE vd; vd.nYear = pTS->year; vd.nMonth = pTS->month; vd.nDay = pTS->day; RspIdentifier.SetDateValue(pElement, &vd, 1); } } } // For the Wild Card Matching: Replaces every '*' with '%' and every '?' with '_'. CString CWorklistSCP::PrepareForWCM(const CString& sValue) const { CString sPreparedValue = sValue; int iLength = sPreparedValue.GetLength(); for (int i = 0; i < iLength; i++) { if (sPreparedValue.GetAt(i) == '*') { sPreparedValue.SetAt(i, '%'); } else if (sPreparedValue.GetAt(i) == '?') { sPreparedValue.SetAt(i, '_'); } } return sPreparedValue; } L_VOID CWorklistSCP::GenerateMatchingQuery(LDicomDS* pReqIdentifier, CString& sUser, CString& sFinSQLQuery) { pDICOMELEMENT pElement; CString sAttValue; CString sSQLQuery; // TAG_ACCESSION_NUMBER pElement = pReqIdentifier->FindFirstElement(NULL, TAG_ACCESSION_NUMBER, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { // Wild Card Matching or Single Value Matching if ((sAttValue.Find('*') >= 0) || (sAttValue.Find('?') >= 0)) { // Wild card matching -- case insensitive sSQLQuery += " And TAG_ACCESSION_NUMBER LIKE '" + PrepareForWCM(sAttValue) + "'"; } else { // Single value matching -- case sensitive sSQLQuery += " And (StrComp([MwlSCPTbl].TAG_ACCESSION_NUMBER, '" + sAttValue + "',0)=0) "; } } } // TAG_MODALITY pElement = pReqIdentifier->FindFirstElement(NULL, TAG_MODALITY, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And (StrComp([MwlSCPTbl].TAG_MODALITY, '" + FilterForSingleValueMatch(sAttValue) + "',0)=0) "; // Single Value Matching } } // TAG_INSTITUTION_NAME pElement = pReqIdentifier->FindFirstElement(NULL, TAG_INSTITUTION_NAME, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { // Wild Card Matching or Single Value Matching if ((sAttValue.Find('*') >= 0) || (sAttValue.Find('?') >= 0)) { // Wild card matching -- case insensitive sSQLQuery += " And TAG_INSTITUTION_NAME LIKE '" + PrepareForWCM(sAttValue) + "'"; } else { // Single Value Matching sSQLQuery += " And (StrComp([MwlSCPTbl].TAG_INSTITUTION_NAME, '" + sAttValue + "',0)=0) "; } } } // TAG_REFERRING_PHYSICIAN_NAME pElement = pReqIdentifier->FindFirstElement(NULL, TAG_REFERRING_PHYSICIAN_NAME, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And TAG_REFERRING_PHYSICIAN_NAME LIKE '" + PrepareForWCM(sAttValue) + "'"; // Wild Card Matching or Single Value Matching // Case Insensitive } } // TAG_PATIENT_NAME pElement = pReqIdentifier->FindFirstElement(NULL, TAG_PATIENT_NAME, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And TAG_PATIENT_NAME LIKE '" + PrepareForWCM(sAttValue) + "'"; // Wild Card Matching or Single Value Matching // Case Insensitive } } // TAG_PATIENT_ID pElement = pReqIdentifier->FindFirstElement(NULL, TAG_PATIENT_ID, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And (StrComp([MwlSCPTbl].TAG_PATIENT_ID, '" + FilterForSingleValueMatch(sAttValue) + "',0)=0) "; // Single Value Matching } } CString sRetCond; // TAG_PATIENT_BIRTH_DATE pElement = pReqIdentifier->FindFirstElement(NULL, TAG_PATIENT_BIRTH_DATE, FALSE); if (pElement) { L_CHAR* pszValue = new L_CHAR[pElement->nLength + 1]; if (pszValue) { if (pReqIdentifier->GetBinaryValue(pElement, pszValue, pElement->nLength)) { pszValue[pElement->nLength] = '\0'; BuildDateCondition(pszValue, "TAG_PATIENT_BIRTH_DATE", sRetCond, FALSE); sSQLQuery += sRetCond; // Range Matching } delete [] pszValue; } } // TAG_PATIENT_SEX pElement = pReqIdentifier->FindFirstElement(NULL, TAG_PATIENT_SEX, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And (StrComp([MwlSCPTbl].TAG_PATIENT_SEX, '" + FilterForSingleValueMatch(sAttValue) + "',0)=0) "; // Single Value Matching } } // TAG_PATIENT_WEIGHT pElement = pReqIdentifier->FindFirstElement(NULL, TAG_PATIENT_WEIGHT, FALSE); if (pElement) { L_DOUBLE* pdValue = pReqIdentifier->GetDoubleValue(pElement, 0, 1); if (pdValue) { sAttValue.Format("%g", *pdValue); sSQLQuery += " And ([MwlSCPTbl].TAG_PATIENT_WEIGHT = " + sAttValue + ")"; } } // TAG_STUDY_INSTANCE_UID GetUIDCondition(pReqIdentifier, TAG_STUDY_INSTANCE_UID, "TAG_STUDY_INSTANCE_UID", sAttValue); // List of UID Matching sSQLQuery += sAttValue; // TAG_REQUESTING_PHYSICIAN pElement = pReqIdentifier->FindFirstElement(NULL, TAG_REQUESTING_PHYSICIAN, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And TAG_REQUESTING_PHYSICIAN LIKE '" + PrepareForWCM(sAttValue) + "'"; // Wild Card Matching or Single Value Matching // Case Insensitive } } // TAG_REQUESTED_PROCEDURE_DESCRIPTION pElement = pReqIdentifier->FindFirstElement(NULL, TAG_REQUESTED_PROCEDURE_DESCRIPTION, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And TAG_REQUESTED_PROCEDURE_DESCRIPTION LIKE '" + PrepareForWCM(sAttValue) + "'"; // Single Value Matching // Case Insensitive } } // TAG_ADMISSION_ID pElement = pReqIdentifier->FindFirstElement(NULL, TAG_ADMISSION_ID, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And (StrComp([MwlSCPTbl].TAG_ADMISSION_ID, '" + FilterForSingleValueMatch(sAttValue) + "',0)=0) "; // Single Value Matching } } // TAG_SCHEDULED_STATION_AE_TITLE pElement = pReqIdentifier->FindFirstElement(NULL, TAG_SCHEDULED_STATION_AE_TITLE, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And (StrComp([MwlSCPTbl].TAG_SCHEDULED_STATION_AE_TITLE, '" + FilterForSingleValueMatch(sAttValue) + "',0)=0) "; // Single Value Matching } } // TAG_SCHEDULED_PROCEDURE_STEP_START_DATE sRetCond.Empty(); pElement = pReqIdentifier->FindFirstElement(NULL, TAG_SCHEDULED_PROCEDURE_STEP_START_DATE, FALSE); if (pElement) { L_CHAR* pszValue = new L_CHAR[pElement->nLength + 1]; if (pszValue) { if (pReqIdentifier->GetBinaryValue(pElement, pszValue, pElement->nLength)) { pszValue[pElement->nLength] = '\0'; BuildDateCondition(pszValue, "TAG_SCHEDULED_PROCEDURE_STEP_START_DATE", sRetCond, FALSE); sSQLQuery += sRetCond; // Range Matching } delete [] pszValue; } } // TAG_SCHEDULED_PROCEDURE_STEP_START_TIME sRetCond.Empty(); pElement = pReqIdentifier->FindFirstElement(NULL, TAG_SCHEDULED_PROCEDURE_STEP_START_TIME, FALSE); if (pElement) { L_CHAR* pszValue = new L_CHAR[pElement->nLength + 1]; if (pszValue) { if (pReqIdentifier->GetBinaryValue(pElement, pszValue, pElement->nLength)) { pszValue[pElement->nLength] = '\0'; BuildDateCondition(pszValue, "TAG_SCHEDULED_PROCEDURE_STEP_START_TIME", sRetCond, TRUE); sSQLQuery += sRetCond; // Range Matching } delete [] pszValue; } } // TAG_SCHEDULED_PERFORMING_PHYSICIAN_NAME pElement = pReqIdentifier->FindFirstElement(NULL, TAG_SCHEDULED_PERFORMING_PHYSICIAN_NAME, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And TAG_SCHEDULED_PERFORMING_PHYSICIAN_NAME LIKE '" + PrepareForWCM(sAttValue) + "'"; // Wild Card Matching or Single Value Matching // Case Insensitive } } // TAG_SCHEDULED_PROCEDURE_STEP_DESCRIPTION pElement = pReqIdentifier->FindFirstElement(NULL, TAG_SCHEDULED_PROCEDURE_STEP_DESCRIPTION, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And TAG_SCHEDULED_PROCEDURE_STEP_DESCRIPTION LIKE '" + PrepareForWCM(sAttValue) + "'"; // Wild Card Matching or Single Value Matching // Case Insensitive } } // TAG_SCHEDULED_PROCEDURE_STEP_ID pElement = pReqIdentifier->FindFirstElement(NULL, TAG_SCHEDULED_PROCEDURE_STEP_ID, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And (StrComp([MwlSCPTbl].TAG_SCHEDULED_PROCEDURE_STEP_ID, '" + FilterForSingleValueMatch(sAttValue) + "',0)=0) "; // Single Value Matching } } // TAG_SCHEDULED_PROCEDURE_STEP_LOCATION pElement = pReqIdentifier->FindFirstElement(NULL, TAG_SCHEDULED_PROCEDURE_STEP_LOCATION, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And TAG_SCHEDULED_PROCEDURE_STEP_LOCATION LIKE '" + PrepareForWCM(sAttValue) + "'"; // Wild Card Matching or Single Value Matching // Case Insensitive } } // TAG_REQUESTED_PROCEDURE_ID pElement = pReqIdentifier->FindFirstElement(NULL, TAG_REQUESTED_PROCEDURE_ID, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And (StrComp([MwlSCPTbl].TAG_REQUESTED_PROCEDURE_ID, '" + FilterForSingleValueMatch(sAttValue) + "',0)=0) "; // Single Value Matching } } // TAG_REASON_FOR_THE_REQUESTED_PROCEDURE pElement = pReqIdentifier->FindFirstElement(NULL, TAG_REASON_FOR_THE_REQUESTED_PROCEDURE, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And (StrComp([MwlSCPTbl].TAG_REASON_FOR_THE_REQUESTED_PROCEDURE, '" + FilterForSingleValueMatch(sAttValue) + "',0)=0) "; // Single Value Matching } } // TAG_REQUESTED_PROCEDURE_PRIORITY pElement = pReqIdentifier->FindFirstElement(NULL, TAG_REQUESTED_PROCEDURE_PRIORITY, FALSE); if (pElement) { sAttValue = pReqIdentifier->GetStringValue(pElement, 0, 1); if (!sAttValue.IsEmpty()) { sSQLQuery += " And (StrComp([MwlSCPTbl].TAG_REQUESTED_PROCEDURE_PRIORITY, '" + FilterForSingleValueMatch(sAttValue) + "',0)=0) "; // Single Value Matching } } sFinSQLQuery = "select * from MwlSCPTbl where true" + sSQLQuery; } CString& CWorklistSCP::FilterForSingleValueMatch(CString& sValue) { sValue.Remove('*'); sValue.Remove('?'); return sValue; } L_VOID CWorklistSCP::GetUIDCondition(LDicomDS* ReqIdentifier, L_UINT32 uTag, const CString& sFieldName, CString& sRetCondition) { // List of UID Matching sRetCondition.Empty(); pDICOMELEMENT pElement = ReqIdentifier->FindFirstElement(NULL, uTag, FALSE); if (!pElement) return; L_UINT32 uUIDsCount = ReqIdentifier->GetCountValue(pElement); if (uUIDsCount >= 1) { sRetCondition = " And ("; } CString sUID; for (L_UINT32 i = 0; i < uUIDsCount; i++) { sUID = ReqIdentifier->GetStringValue(pElement, i, 1); sRetCondition += sFieldName + "='" + sUID + "'"; if (i != uUIDsCount - 1) { sRetCondition += " OR "; } } if (uUIDsCount >= 1) { sRetCondition += ")"; } } L_VOID CWorklistSCP::BuildDateCondition(CString sDateAttribute, const CString& sFieldName, CString& sRetCondition, L_BOOL bTimeVal) { sRetCondition.Empty(); sDateAttribute.TrimLeft(); sDateAttribute.TrimRight(); if (sDateAttribute.IsEmpty()) { return; } // Check if range or not if (!IsRange((L_CHAR*)(LPCTSTR) sDateAttribute)) { // No range sRetCondition = " And (" + sFieldName + " = #" + ConvertDicomDateTimeToAccessDate(sDateAttribute, bTimeVal) + "#)"; return; } // Check if it starts with a '-' then it's an upper range if(sDateAttribute[0] == '-' ) { sRetCondition = " And (" + sFieldName + " <= #" + ConvertDicomDateTimeToAccessDate(sDateAttribute.Right(sDateAttribute.GetLength() - 1), bTimeVal) + "#)"; } // If it ends with a '-' then it's an lower range else if(sDateAttribute[sDateAttribute.GetLength() - 1] == '-') { sRetCondition = " And (" + sFieldName + " >= #" + ConvertDicomDateTimeToAccessDate(sDateAttribute, bTimeVal) + "#)"; } // It's a full range else { CString sDate; L_INT nPos = sDateAttribute.ReverseFind('-'); if (nPos > 0) { sDate = sDateAttribute.Left(nPos); if (sDate.GetLength() > 0) { sRetCondition = " And (" + sFieldName + " Between #" + ConvertDicomDateTimeToAccessDate(sDate, bTimeVal) + "# And #"; sDate = sDateAttribute.Right(sDateAttribute.GetLength() - (nPos + 1)); sRetCondition += ConvertDicomDateTimeToAccessDate(sDate, bTimeVal) + "#) "; } } } } CString CWorklistSCP::ConvertDicomDateTimeToAccessDate(const CString& sDicomDate, L_BOOL bTimeVal) { CString sTemp; CString sAccessDate; L_INT nOffset = 0; sAccessDate.Empty(); if (!bTimeVal) // Date Value { if (sDicomDate.GetLength() < 8) { return sAccessDate; } sTemp = sDicomDate.Left(4); // Year sAccessDate = sTemp + "/"; nOffset += 4; sTemp = sDicomDate.Mid(nOffset, 2); // Month sAccessDate += sTemp + "/"; nOffset += 2; sTemp = sDicomDate.Mid(nOffset, 2); // day sAccessDate += sTemp; } else // Time value { if (sDicomDate.GetLength() < 6) { return sAccessDate; } sTemp = sDicomDate.Left(2); // Hours sAccessDate = sTemp + ":"; nOffset += 2; sTemp = sDicomDate.Mid(nOffset, 2); // Minutes sAccessDate += sTemp + ":"; nOffset += 2; sTemp = sDicomDate.Mid(nOffset, 2); // Seconds sAccessDate += sTemp; } return sAccessDate; } L_BOOL CWorklistSCP::IsRange(L_CHAR* pszValue) { L_CHAR * pTemp = pszValue; if(pTemp) { while(*pTemp) { if(*pTemp == '-') { return TRUE; } pTemp ++; } } return FALSE; } L_VOID CWorklistSCP::OnReceiveCEchoRequest(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_CHAR *pszClass) { L_CHAR* pszCurClientName; LDicomAssociate* pPDU = GetAssociate(); if (!pPDU) { // The connection is not associated, so ignore the message return; } // Get the Calling AE title pszCurClientName = GetAssociate()->GetCalling(); // Status OnStatus(pszCurClientName, "Receiving C-Echo Request..."); L_UINT16 nStatus = COMMAND_STATUS_SUCCESS; pDICOMUID pUID = LDicomUID::Find(pszClass); if (!pUID) { nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; OnStatus(pszCurClientName, "Sending C-Echo Response - Status: Abstract Syntax not supported!", DICOM_SUCCESS); } else { L_UCHAR nID = pPDU->FindAbstract(pszClass); if ((nID == 0) || (pPDU->GetResult(nID) != PDU_ACCEPT_RESULT_SUCCESS)) { nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; OnStatus(pszCurClientName, "Sending C-Echo Response - Status: Abstract Syntax not supported!", DICOM_SUCCESS); } } if (nStatus == COMMAND_STATUS_SUCCESS) { OnStatus(pszCurClientName, "Sending C-Echo Response..."); } SendCEchoResponse(nPresentationID, nMessageID, pszClass, nStatus); } L_VOID CWorklistSCP::OnReceiveAbort(L_UCHAR nSource, L_UCHAR nReason) { L_CHAR* pszCurClientName; // Get the Calling AE title pszCurClientName = GetAssociate()->GetCalling(); // Status OnStatus(pszCurClientName, "Receiving Abort."); // Close the connection of this client Close(); } L_VOID CWorklistSCP::OnClose(L_INT nError, LDicomNet *pNet) { delete (CWorklistSCP*) pNet; }