feat: Add MPPSSCU
This commit is contained in:
269
src/dicom/MPPSSCU.cpp
Normal file
269
src/dicom/MPPSSCU.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#include "MPPSSCU.h"
|
||||
|
||||
#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
|
||||
#include "dcmtk/dcmnet/diutil.h" /* for dcmnet logger */
|
||||
#include "dcmtk/dcmdata/dcuid.h" /* for dcmFindUIDName() */
|
||||
#include "dcmtk/dcmdata/dcostrmf.h" /* for class DcmOutputFileStream */
|
||||
#include "dcmtk/ofstd/ofmem.h" /* for OFunique_ptr */
|
||||
|
||||
Uint16 MPPSSCU::nextMessageID()
|
||||
{
|
||||
if (!isConnected())
|
||||
return 0;
|
||||
else
|
||||
return messageID++;
|
||||
}
|
||||
|
||||
OFCondition MPPSSCU::sendNCreateRequest(const T_ASC_PresentationContextID presID,
|
||||
const OFString &sopInstanceUID,
|
||||
DcmDataset *reqDataset,
|
||||
Uint16 &rspStatusCode)
|
||||
{
|
||||
// Do some basic validity checks
|
||||
if (!isConnected())
|
||||
return DIMSE_ILLEGALASSOCIATION;
|
||||
if (sopInstanceUID.empty() || (reqDataset == NULL))
|
||||
return DIMSE_NULLKEY;
|
||||
|
||||
// Prepare DIMSE data structures for issuing request
|
||||
OFCondition cond;
|
||||
OFString tempStr;
|
||||
T_ASC_PresentationContextID pcid = presID;
|
||||
T_DIMSE_Message request;
|
||||
// Make sure everything is zeroed (especially options)
|
||||
bzero((char *)&request, sizeof(request));
|
||||
T_DIMSE_N_CreateRQ &actionReq = request.msg.NCreateRQ;
|
||||
DcmDataset *statusDetail = NULL;
|
||||
|
||||
request.CommandField = DIMSE_N_CREATE_RQ;
|
||||
actionReq.MessageID = nextMessageID();
|
||||
actionReq.DataSetType = DIMSE_DATASET_PRESENT;
|
||||
|
||||
// Determine SOP Class from presentation context
|
||||
OFString abstractSyntax, transferSyntax;
|
||||
findPresentationContext(pcid, abstractSyntax, transferSyntax);
|
||||
if (abstractSyntax.empty() || transferSyntax.empty())
|
||||
return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
|
||||
OFStandard::strlcpy(actionReq.AffectedSOPClassUID, abstractSyntax.c_str(), sizeof(actionReq.AffectedSOPClassUID));
|
||||
if (sopInstanceUID.size() > 0)
|
||||
{
|
||||
OFStandard::strlcpy(actionReq.AffectedSOPInstanceUID, sopInstanceUID.c_str(), sizeof(actionReq.AffectedSOPInstanceUID));
|
||||
actionReq.opts = O_NCREATE_AFFECTEDSOPINSTANCEUID;
|
||||
}
|
||||
else
|
||||
{
|
||||
actionReq.AffectedSOPInstanceUID[0] = 0;
|
||||
actionReq.opts = 0;
|
||||
}
|
||||
|
||||
// Send request
|
||||
// if (DCM_dcmnetLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL))
|
||||
// {
|
||||
// DCMNET_INFO("Sending N-ACTION Request");
|
||||
// // Output dataset only if trace level is enabled
|
||||
// if (DCM_dcmnetLogger.isEnabledFor(OFLogger::TRACE_LOG_LEVEL))
|
||||
// DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, request, DIMSE_OUTGOING, reqDataset, pcid));
|
||||
// else
|
||||
// DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, request, DIMSE_OUTGOING, NULL, pcid));
|
||||
// } else {
|
||||
// DCMNET_INFO("Sending N-ACTION Request (MsgID " << actionReq.MessageID << ")");
|
||||
// }
|
||||
cond = sendDIMSEMessage(pcid, &request, reqDataset);
|
||||
if (cond.bad())
|
||||
{
|
||||
DCMNET_ERROR("Failed sending N-Create request: " << DimseCondition::dump(tempStr, cond));
|
||||
return cond;
|
||||
}
|
||||
|
||||
// Receive response
|
||||
T_DIMSE_Message response;
|
||||
bzero((char *)&response, sizeof(response));
|
||||
cond = receiveDIMSECommand(&pcid, &response, &statusDetail, NULL /* commandSet */);
|
||||
if (cond.bad())
|
||||
{
|
||||
DCMNET_ERROR("Failed receiving DIMSE response: " << DimseCondition::dump(tempStr, cond));
|
||||
return cond;
|
||||
}
|
||||
|
||||
// Check command set
|
||||
if (response.CommandField == DIMSE_N_CREATE_RSP)
|
||||
{
|
||||
// if (DCM_dcmnetLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL))
|
||||
// {
|
||||
// DCMNET_INFO("Received N-ACTION Response");
|
||||
// DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, response, DIMSE_INCOMING, NULL, pcid));
|
||||
// } else {
|
||||
// DCMNET_INFO("Received N-ACTION Response (" << DU_nactionStatusString(response.msg.NActionRSP.DimseStatus) << ")");
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
DCMNET_ERROR("Expected N-ACTION response but received DIMSE command 0x"
|
||||
<< STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
|
||||
<< OFstatic_cast(unsigned int, response.CommandField));
|
||||
DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, response, DIMSE_INCOMING, NULL, pcid));
|
||||
delete statusDetail;
|
||||
return DIMSE_BADCOMMANDTYPE;
|
||||
}
|
||||
if (statusDetail != NULL)
|
||||
{
|
||||
DCMNET_DEBUG("Response has status detail:" << OFendl << DcmObject::PrintHelper(*statusDetail));
|
||||
delete statusDetail;
|
||||
}
|
||||
|
||||
// Set return value
|
||||
T_DIMSE_N_CreateRSP &actionRsp = response.msg.NCreateRSP;
|
||||
rspStatusCode = actionRsp.DimseStatus;
|
||||
|
||||
// Check whether there is a dataset to be received
|
||||
if (actionRsp.DataSetType == DIMSE_DATASET_PRESENT)
|
||||
{
|
||||
// this should never happen
|
||||
DcmDataset *tempDataset = NULL;
|
||||
T_ASC_PresentationContextID tempID;
|
||||
// DCMNET_WARN("Trying to retrieve unexpected dataset in N-ACTION response");
|
||||
cond = receiveDIMSEDataset(&tempID, &tempDataset);
|
||||
if (cond.good())
|
||||
{
|
||||
// DCMNET_WARN("Received unexpected dataset after N-ACTION response, ignoring");
|
||||
delete tempDataset;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DIMSE_BADDATA;
|
||||
}
|
||||
}
|
||||
if (actionRsp.MessageIDBeingRespondedTo != actionReq.MessageID)
|
||||
{
|
||||
// since we only support synchronous communication, the message ID in the response
|
||||
// should be identical to the one in the request
|
||||
// DCMNET_ERROR("Received response with wrong message ID (" << actionRsp.MessageIDBeingRespondedTo
|
||||
// << " instead of " << actionReq.MessageID << ")");
|
||||
return DIMSE_BADMESSAGE;
|
||||
}
|
||||
|
||||
return cond;
|
||||
}
|
||||
|
||||
OFCondition MPPSSCU::sendNSetRequest(const T_ASC_PresentationContextID presID,
|
||||
const OFString &sopInstanceUID,
|
||||
DcmDataset *reqDataset,
|
||||
Uint16 &rspStatusCode)
|
||||
{
|
||||
// Do some basic validity checks
|
||||
if (!isConnected())
|
||||
return DIMSE_ILLEGALASSOCIATION;
|
||||
if (sopInstanceUID.empty() || (reqDataset == NULL))
|
||||
return DIMSE_NULLKEY;
|
||||
|
||||
// Prepare DIMSE data structures for issuing request
|
||||
OFCondition cond;
|
||||
OFString tempStr;
|
||||
T_ASC_PresentationContextID pcid = presID;
|
||||
T_DIMSE_Message request;
|
||||
// Make sure everything is zeroed (especially options)
|
||||
bzero((char *)&request, sizeof(request));
|
||||
T_DIMSE_N_SetRQ &actionReq = request.msg.NSetRQ;
|
||||
DcmDataset *statusDetail = NULL;
|
||||
|
||||
request.CommandField = DIMSE_N_SET_RQ;
|
||||
actionReq.MessageID = nextMessageID();
|
||||
actionReq.DataSetType = DIMSE_DATASET_PRESENT;
|
||||
|
||||
// Determine SOP Class from presentation context
|
||||
OFString abstractSyntax, transferSyntax;
|
||||
findPresentationContext(pcid, abstractSyntax, transferSyntax);
|
||||
if (abstractSyntax.empty() || transferSyntax.empty())
|
||||
return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
|
||||
OFStandard::strlcpy(actionReq.RequestedSOPClassUID, abstractSyntax.c_str(), sizeof(actionReq.RequestedSOPClassUID));
|
||||
|
||||
OFStandard::strlcpy(actionReq.RequestedSOPInstanceUID, sopInstanceUID.c_str(), sizeof(actionReq.RequestedSOPInstanceUID));
|
||||
|
||||
// Send request
|
||||
// if (DCM_dcmnetLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL))
|
||||
// {
|
||||
// DCMNET_INFO("Sending N-ACTION Request");
|
||||
// // Output dataset only if trace level is enabled
|
||||
// if (DCM_dcmnetLogger.isEnabledFor(OFLogger::TRACE_LOG_LEVEL))
|
||||
// DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, request, DIMSE_OUTGOING, reqDataset, pcid));
|
||||
// else
|
||||
// DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, request, DIMSE_OUTGOING, NULL, pcid));
|
||||
// } else {
|
||||
// DCMNET_INFO("Sending N-ACTION Request (MsgID " << actionReq.MessageID << ")");
|
||||
// }
|
||||
cond = sendDIMSEMessage(pcid, &request, reqDataset);
|
||||
if (cond.bad())
|
||||
{
|
||||
DCMNET_ERROR("Failed sending N-Create request: " << DimseCondition::dump(tempStr, cond));
|
||||
return cond;
|
||||
}
|
||||
|
||||
// Receive response
|
||||
T_DIMSE_Message response;
|
||||
bzero((char *)&response, sizeof(response));
|
||||
cond = receiveDIMSECommand(&pcid, &response, &statusDetail, NULL /* commandSet */);
|
||||
if (cond.bad())
|
||||
{
|
||||
DCMNET_ERROR("Failed receiving DIMSE response: " << DimseCondition::dump(tempStr, cond));
|
||||
return cond;
|
||||
}
|
||||
|
||||
// Check command set
|
||||
if (response.CommandField == DIMSE_N_SET_RSP)
|
||||
{
|
||||
// if (DCM_dcmnetLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL))
|
||||
// {
|
||||
// DCMNET_INFO("Received N-ACTION Response");
|
||||
// DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, response, DIMSE_INCOMING, NULL, pcid));
|
||||
// } else {
|
||||
// DCMNET_INFO("Received N-ACTION Response (" << DU_nactionStatusString(response.msg.NActionRSP.DimseStatus) << ")");
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
DCMNET_ERROR("Expected N-SET response but received DIMSE command 0x"
|
||||
<< STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
|
||||
<< OFstatic_cast(unsigned int, response.CommandField));
|
||||
DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, response, DIMSE_INCOMING, NULL, pcid));
|
||||
delete statusDetail;
|
||||
return DIMSE_BADCOMMANDTYPE;
|
||||
}
|
||||
if (statusDetail != NULL)
|
||||
{
|
||||
DCMNET_DEBUG("Response has status detail:" << OFendl << DcmObject::PrintHelper(*statusDetail));
|
||||
delete statusDetail;
|
||||
}
|
||||
|
||||
// Set return value
|
||||
T_DIMSE_N_SetRSP &actionRsp = response.msg.NSetRSP;
|
||||
rspStatusCode = actionRsp.DimseStatus;
|
||||
|
||||
// Check whether there is a dataset to be received
|
||||
if (actionRsp.DataSetType == DIMSE_DATASET_PRESENT)
|
||||
{
|
||||
// this should never happen
|
||||
DcmDataset *tempDataset = NULL;
|
||||
T_ASC_PresentationContextID tempID;
|
||||
DCMNET_WARN("Trying to retrieve unexpected dataset in N-SET response");
|
||||
cond = receiveDIMSEDataset(&tempID, &tempDataset);
|
||||
if (cond.good())
|
||||
{
|
||||
DCMNET_WARN("Received unexpected dataset after N-SET response, ignoring");
|
||||
delete tempDataset;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DIMSE_BADDATA;
|
||||
}
|
||||
}
|
||||
if (actionRsp.MessageIDBeingRespondedTo != actionReq.MessageID)
|
||||
{
|
||||
// since we only support synchronous communication, the message ID in the response
|
||||
// should be identical to the one in the request
|
||||
DCMNET_ERROR("Received response with wrong message ID (" << actionRsp.MessageIDBeingRespondedTo
|
||||
<< " instead of " << actionReq.MessageID << ")");
|
||||
return DIMSE_BADMESSAGE;
|
||||
}
|
||||
|
||||
return cond;
|
||||
}
|
||||
25
src/dicom/MPPSSCU.h
Normal file
25
src/dicom/MPPSSCU.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef E087D231_BC4C_4D23_9152_F51B7B4509FB
|
||||
#define E087D231_BC4C_4D23_9152_F51B7B4509FB
|
||||
#include "dcmtk/dcmnet/scu.h"
|
||||
|
||||
class DCMTK_DCMNET_EXPORT MPPSSCU : public DcmSCU
|
||||
{
|
||||
|
||||
public:
|
||||
MPPSSCU() = default;
|
||||
virtual ~MPPSSCU() = default;
|
||||
virtual OFCondition sendNCreateRequest(const T_ASC_PresentationContextID presID,
|
||||
const OFString &sopInstanceUID,
|
||||
DcmDataset *reqDataset,
|
||||
Uint16 &rspStatusCode);
|
||||
virtual OFCondition sendNSetRequest(const T_ASC_PresentationContextID presID,
|
||||
const OFString &sopInstanceUID,
|
||||
DcmDataset *reqDataset,
|
||||
Uint16 &rspStatusCode);
|
||||
|
||||
private:
|
||||
Uint16 nextMessageID();
|
||||
Uint16 messageID = 0;
|
||||
};
|
||||
|
||||
#endif /* E087D231_BC4C_4D23_9152_F51B7B4509FB */
|
||||
Reference in New Issue
Block a user