ImFusion SDK 4.3
PacsExample.cpp
#include <ImFusion/Core/Filesystem/Url.h>
#include <ImFusion/Core/Log.h>
#include <ImFusion/Dicom/DicomPacsCommunication.h>
#include <dcmtk/dcmdata/dctk.h>
#include <iostream>
#include <memory>
#include <vector>
int main(int argc, char** argv)
{
// A simple application that demos the PACS utilities in the ImFusionLib.
// It queries all patients, all studies and all series of a pacs and displays
// the results on the console.
// The easiest way to test this, is to setup an orthanc server on your local
// machine, upload some DICOM files and then run this example.
//
// The application expects two arguments: the hostname including the port of the server
// and the Application Entity (AE) title of the server.
// Optionally a third integer argument can be passed, which will load the images of
// the series with that index.
//
// E.g. with the default orthanc configuration:
// ./PacsExample http://localhost:4242 ORTHANC 0
if (argc < 3)
{
std::cout << "Two arguments are expected:" << std::endl;
std::cout << "The server url including port (e.g. http://localhost:8042)" << std::endl;
std::cout << "The AE title of the server" << std::endl;
return 1;
}
int loadSeries = -1;
if (argc >= 4)
loadSeries = std::atoi(argv[3]);
OFLog::configure(OFLogger::LogLevel::ERROR_LOG_LEVEL);
ImFusion::Filesystem::Url serverUrl(argv[1]);
if (!serverUrl.isValid())
{
std::cout << "The server url is not valid" << std::endl;
return 2;
}
const std::string serverAeTitle(argv[2]);
if (serverAeTitle.empty())
{
std::cout << "The AE Title must not be empty" << std::endl;
return 3;
}
ImFusion::DicomPacsConfiguration config(serverUrl.host(), std::stoi(serverUrl.port()), serverAeTitle);
// Those values are initialized with the values in DicomPluginSettings by default.
// The settings require a QApplication, which we are not using here so we set the
// values explicitly
config.clientAETitle = "IMFDCMCLIENT"; // can be anything but has to be known to the PACS server
config.clientPort = 2000;
config.connectionTimeout = 10;
if (!config.isValid())
{
std::cout << "The PACS configuration is not valid" << std::endl;
return 4;
}
// Check whether we can connect to the server at all
if (dcp.sendEchoRequest())
std::cout << "Echo request successful!" << std::endl;
else
{
std::cout << "Echo request failed!" << std::endl;
return 5;
}
// Perform some C-FIND requests to get a list of patients, studies or series
DcmDataset request;
// Request the following tags without any filtering.
// If you want to e.g. filter by patient name, just enter the search term for DCM_PatientName.
request.putAndInsertOFStringArray(DCM_StudyInstanceUID, "");
request.putAndInsertOFStringArray(DCM_StudyID, "");
request.putAndInsertOFStringArray(DCM_StudyTime, "");
request.putAndInsertOFStringArray(DCM_StudyDate, "");
request.putAndInsertOFStringArray(DCM_StudyDescription, "");
request.putAndInsertOFStringArray(DCM_PatientID, "");
request.putAndInsertOFStringArray(DCM_PatientName, "");
request.putAndInsertOFStringArray(DCM_PatientSex, "");
request.putAndInsertOFStringArray(DCM_PatientBirthDate, "");
request.putAndInsertOFStringArray(DCM_PatientComments, "");
{
// Request all patients from the PACS
request.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "PATIENT");
dcp.sendFindRequest(ImFusion::DicomPacsCommunication::PatientRootRetrieveInformationModel, request, [&](auto dataset) {
if (dataset)
datasets.push_back(std::move(dataset));
});
std::cout << "##########################################" << std::endl;
std::cout << "Received " << datasets.size() << " patients" << std::endl;
std::cout << "##########################################" << std::endl;
for (const auto& dataset : datasets)
dataset->print(std::cout);
}
{
// Request all studies from the PACS
request.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "STUDY");
dcp.sendFindRequest(ImFusion::DicomPacsCommunication::StudyRootRetrieveInformationModel, request, [&](auto dataset) {
if (dataset)
datasets.push_back(std::move(dataset));
});
std::cout << "##########################################" << std::endl;
std::cout << "Received " << datasets.size() << " studies" << std::endl;
std::cout << "##########################################" << std::endl;
for (const auto& dataset : datasets)
dataset->print(std::cout);
}
{
// Request all series from the PACS
request.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "SERIES");
request.putAndInsertOFStringArray(DCM_SeriesInstanceUID, "");
request.putAndInsertOFStringArray(DCM_SeriesNumber, "");
request.putAndInsertOFStringArray(DCM_SeriesTime, "");
request.putAndInsertOFStringArray(DCM_SeriesDate, "");
request.putAndInsertOFStringArray(DCM_SeriesDescription, "");
dcp.sendFindRequest(ImFusion::DicomPacsCommunication::StudyRootRetrieveInformationModel, request, [&](auto dataset) {
if (dataset)
{
std::string seriesUID;
dataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesUID);
seriesUIDs.push_back(seriesUID);
datasets.push_back(std::move(dataset));
}
});
std::cout << "##########################################" << std::endl;
std::cout << "Received " << datasets.size() << " series" << std::endl;
std::cout << "##########################################" << std::endl;
for (const auto& dataset : datasets)
dataset->print(std::cout);
}
if (loadSeries >= 0 && loadSeries < seriesUIDs.size())
{
// Load the images of the selected series.
// This performs a C-MOVE request which requires the server to connect to the client
// on the configured hostname or port. This might fail because of a lot of reasons:
// - The server doesn't know the AE title of the client (has to be configured on the server)
// - The request is blocked by a firewall
// - The client port is already used by another application
// - The client isn't in the same network and not reachable from the outside (e.g. the request is blocked by the router)
// - Many other network related problems...
DcmDataset moveRequest;
// Only request images of a particular series
moveRequest.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "SERIES");
moveRequest.putAndInsertOFStringArray(DCM_SeriesInstanceUID, seriesUIDs[loadSeries]);
dcp.sendMoveRequest(ImFusion::DicomPacsCommunication::StudyRootRetrieveInformationModel, moveRequest, [&](auto dataset) {
if (dataset)
datasets.push_back(std::move(dataset));
});
std::cout << "##########################################" << std::endl;
std::cout << "Received " << datasets.size() << " images for series " << seriesUIDs[loadSeries] << std::endl;
std::cout << "##########################################" << std::endl;
for (const auto& dataset : datasets)
dataset->print(std::cout, DCMTypes::PF_shortenLongTagValues);
}
return 0;
}
T atoi(T... args)
Class facilitating the communication with DICOM PACS servers.
Definition DicomPacsCommunication.h:94
void sendFindRequest(RetrieveInformationModel queryModel, DcmDataset &request, std::function< void(std::unique_ptr< DcmDataset >)> responseItemCallback, std::atomic< bool > *abortFlag=nullptr)
Synchronously sends a C-FIND request to the currently connected PACS server.
void sendMoveRequest(RetrieveInformationModel queryModel, DcmDataset &request, std::function< void(std::unique_ptr< DcmDataset >)> responseItemCallback)
Synchronously sends a C-MOVE request to the currently connected PACS server.
bool sendEchoRequest()
Connects to the configured PACS and sends a C-ECHO message in order to test the association.
Entity representing a URL (Uniform Resource Locator).
Definition Url.h:83
bool isValid() const
Checks if the URL can be interpreted as valid.
std::string host() const
Returns the host of the URL (part of the authority).
std::string port() const
Returns the port of the URL (part of the authority).
T empty(T... args)
T endl(T... args)
void init(Level globalMinimumLevel, Mode operationMode)
Initialize the logging framework.
void deinit()
Deinitialize the logging framework.
@ Warning
Error messages where the emitting code can continue but the result might be unexpected/bad.
Definition Log.h:28
@ Synchronous
All log events are synchronously forwarded to all registered loggers.
Definition Log.h:38
T push_back(T... args)
T size(T... args)
T stoi(T... args)
Structure encapsulating all information needed to connect to a PACS instance.
Definition DicomPacsCommunication.h:27
std::string clientAETitle
Application Entity to use by the DICOM SCP server for receiving images.
Definition DicomPacsCommunication.h:57
bool isValid() const
Returns true if the configuration is valid, e.g.
int connectionTimeout
Connection Timeout (seconds)
Definition DicomPacsCommunication.h:60
unsigned int clientPort
Port to use by the DICOM SCP server for receiving images.
Definition DicomPacsCommunication.h:56
Search Tab / S to search, Esc to close