feat: Add MPPSSCU

This commit is contained in:
chenhuijun
2024-06-06 13:35:05 +08:00
parent 6ef5571f25
commit 2e520ac73a
2 changed files with 294 additions and 0 deletions

269
src/dicom/MPPSSCU.cpp Normal file
View 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
View 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 */