Skip to content

Commit

Permalink
ENH: Implement WADO-URI. #2866
Browse files Browse the repository at this point in the history
At the PACS level, since WADO-URI is only for retrieving instances, a
new PACS type has been added: WADO-URI + DIMSE. It uses WADO-URI for the
retrieve and DIMSE for the rest (searches and stores).
  • Loading branch information
Woundorf committed Jun 30, 2022
1 parent 0e48b0b commit e4e3b8f
Show file tree
Hide file tree
Showing 35 changed files with 1,095 additions and 131 deletions.
26 changes: 19 additions & 7 deletions starviewer/src/core/pacsdevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,20 @@ bool PacsDevice::isEmpty() const

bool PacsDevice::isSamePacsDevice(const PacsDevice &pacsDevice) const
{
return (m_type == Type::Dimse
&& m_AETitle == pacsDevice.getAETitle()
&& m_address == pacsDevice.getAddress()
&& m_queryRetrieveServicePort == pacsDevice.getQueryRetrieveServicePort())
|| (m_type == Type::Wado
&& m_baseUri == pacsDevice.getBaseUri());
return m_type == pacsDevice.getType()
&& (
( m_type == Type::Dimse
&& m_AETitle == pacsDevice.getAETitle()
&& m_address == pacsDevice.getAddress()
&& m_queryRetrieveServicePort == pacsDevice.getQueryRetrieveServicePort())
|| ( m_type == Type::Wado
&& m_baseUri == pacsDevice.getBaseUri())
|| ( m_type == Type::WadoUriDimse
&& m_AETitle == pacsDevice.getAETitle()
&& m_address == pacsDevice.getAddress()
&& m_queryRetrieveServicePort == pacsDevice.getQueryRetrieveServicePort()
&& m_baseUri == pacsDevice.getBaseUri())
);
}

bool PacsDevice::operator==(const PacsDevice &pacsDevice) const
Expand All @@ -219,10 +227,14 @@ QString PacsDevice::getKeyName() const
{
return m_AETitle + m_address + ":" + QString::number(m_queryRetrieveServicePort);
}
else // m_type == Type::Wado
else if (m_type == Type::Wado)
{
return m_baseUri.toString();
}
else // m_type == Type::WadoUriDimse
{
return m_baseUri.toString() + "𒉖" + m_AETitle + m_address + ":" + QString::number(m_queryRetrieveServicePort);
}
}

QStringList PacsDevice::getDefaultPACSKeyNamesList() const
Expand Down
55 changes: 29 additions & 26 deletions starviewer/src/core/pacsdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ namespace udg {
/**
* @brief The PacsDevice class stores data about a PACS server.
*
* This data includes connexion data (AE Title, address, query/retrieve port and store port for DIMSE PACS, base URI for WADO PACS) and other descriptive data
* (institution, location and description).
* This data includes connexion data and other descriptive data (institution, location and description).
*
* There are three types of PACS according to the protocols they use to search, download and upload studies:
* - DIMSE: C-FIND + C-MOVE + C-STORE
* - WADO: QIDO-RS + WADO-RS + STOW-RS
* - Hybrid WADO-URI + DIMSE: C-FIND + WADO-URI + C-STORE
*/
class PacsDevice {
public:
/// Type of PACS.
enum class Type { Dimse, Wado };
enum class Type { Dimse, Wado, WadoUriDimse };

/// Creates an invalid or empty PACS.
PacsDevice();
Expand All @@ -44,39 +48,39 @@ class PacsDevice {
/// Sets the type of this PACS.
void setType(Type type);

/// Returns the AE title. Only applicable to DIMSE PACS.
/// Returns the AE title. Not applicable to WADO PACS.
const QString& getAETitle() const;
/// Sets the AE title. Only applicable to DIMSE PACS.
/// Sets the AE title. Not applicable to WADO PACS.
void setAETitle(QString aeTitle);

/// Returns the address (IP or hostname). Only applicable to DIMSE PACS.
/// Returns the address (IP or hostname). Not applicable to WADO PACS.
const QString& getAddress() const;
/// Sets the address (IP or hostname). Only applicable to DIMSE PACS.
/// Sets the address (IP or hostname). Not applicable to WADO PACS.
void setAddress(QString address);

/// Returns true if this PACS has the query/retrieve service enabled and false otherwise. Only applicable to DIMSE PACS.
/// Returns true if this PACS has the query/retrieve service enabled and false otherwise. Not applicable to WADO PACS.
bool isQueryRetrieveServiceEnabled() const;
/// Sets whether this PACS has the query/retrieve service enabled or not. Only applicable to DIMSE PACS.
/// Sets whether this PACS has the query/retrieve service enabled or not. Not applicable to WADO PACS.
void setQueryRetrieveServiceEnabled(bool enabled);

/// Returns the port for the query/retrieve service. Only applicable to DIMSE PACS.
/// Returns the port for the query/retrieve service. Not applicable to WADO PACS.
int getQueryRetrieveServicePort() const;
/// Sets the port for the query/retrieve service. Only applicable to DIMSE PACS.
/// Sets the port for the query/retrieve service. Not applicable to WADO PACS.
void setQueryRetrieveServicePort(int port);

/// Returns true if this PACS has the store service enabled and false otherwise. Only applicable to DIMSE PACS.
/// Returns true if this PACS has the store service enabled and false otherwise. Not applicable to WADO PACS.
bool isStoreServiceEnabled() const;
/// Sets whether this PACS has the store service enabled or not. Only applicable to DIMSE PACS.
/// Sets whether this PACS has the store service enabled or not. Not applicable to WADO PACS.
void setStoreServiceEnabled(bool enabled);

/// Returns the port for the store service. Only applicable to DIMSE PACS.
/// Returns the port for the store service. Not applicable to WADO PACS.
int getStoreServicePort() const;
/// Sets the port for the store service. Only applicable to DIMSE PACS.
/// Sets the port for the store service. Not applicable to WADO PACS.
void setStoreServicePort(int port);

/// Returns the base URI. Only applicable to WADO PACS.
/// Returns the base URI. Not applicable to DIMSE PACS.
const QUrl& getBaseUri() const;
/// Sets the base URI. Only applicable to WADO PACS.
/// Sets the base URI. Not applicable to DIMSE PACS.
void setBaseUri(QUrl baseUri);

/// Returns the institution that owns or manages this PACS.
Expand All @@ -102,8 +106,7 @@ class PacsDevice {
/// Returns true if this PACS is empty (default constructed) and false otherwise.
bool isEmpty() const;

/// Returns true if this PACS represents the same as the given one, i.e. same AE Title, address and Q/R port for DIMSE PACS and same base URI for WADO PACS,
/// and false otherwise.
/// Returns true if this PACS represents the same as the given one and false otherwise, comparing only type and query and download connection settings.
bool isSamePacsDevice(const PacsDevice &pacsDevice) const;

/// Returns true if this PacsDevice instance is exactly equal to the given one.
Expand All @@ -121,19 +124,19 @@ class PacsDevice {
QString m_id;
/// Type of this PACS.
Type m_type;
/// AE Title. Only applicable to DIMSE PACS.
/// AE Title. Only applicable to DIMSE and hybrid PACS.
QString m_AETitle;
/// Address (IP or hostname). Only applicable to DIMSE PACS.
/// Address (IP or hostname). Only applicable to DIMSE and hybrid PACS.
QString m_address;
/// Whether the query/retrieve service is enabled or not. Only applicable to DIMSE PACS.
/// Whether the query/retrieve service is enabled or not. Only applicable to DIMSE and hybrid PACS.
bool m_isQueryRetrieveServiceEnabled;
/// Port for the query/retrieve service. Only applicable to DIMSE PACS.
/// Port for the query/retrieve service. Only applicable to DIMSE and hybrid PACS.
int m_queryRetrieveServicePort;
/// Whether the store service is enabled or not. Only applicable to DIMSE PACS.
/// Whether the store service is enabled or not. Only applicable to DIMSE and hybrid PACS.
bool m_isStoreServiceEnabled;
/// Port for the store service. Only applicable to DIMSE PACS.
/// Port for the store service. Only applicable to DIMSE and hybrid PACS.
int m_storeServicePort;
/// Base URI. Only applicable to WADO PACS.
/// Base URI. Only applicable to WADO and hybrid PACS.
QUrl m_baseUri;
/// Institution that owns or manages this PACS.
QString m_institution;
Expand Down
2 changes: 1 addition & 1 deletion starviewer/src/core/starviewerapplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const QString StarviewerBuildPlatform("Linux");
#endif

// Indica per aquesta versió d'starviewer quina és la revisió de bd necessària
const int StarviewerDatabaseRevisionRequired(9596);
const int StarviewerDatabaseRevisionRequired(9597);

const QString OrganizationNameString("GILab");
const QString OrganizationDomainString("starviewer.udg.edu");
Expand Down
2 changes: 2 additions & 0 deletions starviewer/src/inputoutput/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ set(SOURCES
wadosearchstudyoperationresult.cpp
wadostorerequest.cpp
wadostorestudyoperationresult.cpp
wadourirequest.cpp
wadouristudyoperationresult.cpp
)

if(WIN32)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ void DimseQueryStudyOperationResult::onJobFinished()
setSeries(m_job->getSeriesList());
break;
case RequestLevel::Instances:
setInstances(m_job->getImageList());
setInstances(m_job->getImageList(), m_job->getSeriesList());
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion starviewer/src/inputoutput/echotopacstest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ QList<PacsDevice> EchoToPACSTest::getPacsDeviceList()

DiagnosisTestProblem EchoToPACSTest::echo(const PacsDevice &pacs)
{
if (pacs.getType() == PacsDevice::Type::Dimse)
if (pacs.getType() == PacsDevice::Type::Dimse || pacs.getType() == PacsDevice::Type::WadoUriDimse)
{
EchoToPACS echoToPACS;

Expand Down
9 changes: 6 additions & 3 deletions starviewer/src/inputoutput/externalstudyrequestmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,14 @@ QString getPacsIdentificationString(const PacsDevice &pacs)
{
return id + pacs.getAETitle();
}
else
else if (pacs.getType() == PacsDevice::Type::Wado)
{
return id + pacs.getBaseUri().toDisplayString();
}
else // pacs.getType() == PacsDevice::Type::WadoUriDimse
{
return id + "(hybrid) " + pacs.getBaseUri().toDisplayString() + " / " + pacs.getAETitle();
}
}

}
Expand Down Expand Up @@ -163,8 +167,7 @@ void ExternalStudyRequestManager::queryPacsForRequest(DicomMask maskRISRequest)
// TODO Ara mateix cal que nosaltres mateixos fem aquesta comprovació però potser seria interessant que el mètode PACSDevicemanager::queryStudy()
// fes aquesta comprovació i ens retornes algun codi que pugui descriure com ha anat la consulta i així poder actuar en conseqüència mostrant
// un message box, fent un log o el que calgui segons la ocasió.
QList<PacsDevice> queryablePACS =
PacsDeviceManager::getPacsList(PacsDeviceManager::DimseWithQueryRetrieveService | PacsDeviceManager::Wado | PacsDeviceManager::OnlyDefault);
QList<PacsDevice> queryablePACS = PacsDeviceManager::getPacsList(PacsDeviceManager::CanRetrieve | PacsDeviceManager::OnlyDefault);
if (queryablePACS.isEmpty())
{
QMessageBox::information(nullptr, ApplicationNameString, tr("Cannot retrieve the studies requested from SAP, RIS or command line because there is no "
Expand Down
30 changes: 28 additions & 2 deletions starviewer/src/inputoutput/localdatabasepacsretrievedimagesdal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,21 @@ qlonglong LocalDatabasePACSRetrievedImagesDAL::insert(const PacsDevice &pacsDevi
query.bindValue(":address", pacsDevice.getAddress());
query.bindValue(":queryPort", pacsDevice.getQueryRetrieveServicePort());
}
else // WADO
else if (pacsDevice.getType() == PacsDevice::Type::Wado)
{
query.prepare("INSERT INTO PACSRetrievedImages (Type, BaseUri) VALUES (:type, :baseUri)");
query.bindValue(":type", "WADO");
query.bindValue(":baseUri", pacsDevice.getBaseUri());
}
else // WADO-URI + DIMSE
{
query.prepare("INSERT INTO PACSRetrievedImages (Type, AETitle, Address, QueryPort, BaseUri) VALUES (:type, :aeTitle, :address, :queryPort, :baseUri)");
query.bindValue(":type", "WADO-URI+DIMSE");
query.bindValue(":aeTitle", pacsDevice.getAETitle());
query.bindValue(":address", pacsDevice.getAddress());
query.bindValue(":queryPort", pacsDevice.getQueryRetrieveServicePort());
query.bindValue(":baseUri", pacsDevice.getBaseUri());
}

if (executeQueryAndLogError(query))
{
Expand Down Expand Up @@ -81,6 +90,15 @@ PacsDevice LocalDatabasePACSRetrievedImagesDAL::query(qlonglong pacsId)
pacsDevice.setType(PacsDevice::Type::Wado);
pacsDevice.setBaseUri(query.value("BaseUri").toUrl());
}
else if (type == "WADO-URI+DIMSE")
{
pacsDevice.setID(query.value("ID").toString());
pacsDevice.setType(PacsDevice::Type::WadoUriDimse);
pacsDevice.setAETitle(query.value("AETitle").toString());
pacsDevice.setAddress(query.value("Address").toString());
pacsDevice.setQueryRetrieveServicePort(query.value("QueryPort").toInt());
pacsDevice.setBaseUri(query.value("BaseUri").toUrl());
}
else
{
WARN_LOG(QString("Found PACS in database with unexpected type: %1").arg(type));
Expand All @@ -101,11 +119,19 @@ QVariant LocalDatabasePACSRetrievedImagesDAL::queryId(const PacsDevice &pacsDevi
query.bindValue(":address", pacsDevice.getAddress());
query.bindValue(":queryPort", pacsDevice.getQueryRetrieveServicePort());
}
else // WADO
else if (pacsDevice.getType() == PacsDevice::Type::Wado)
{
query.prepare("SELECT ID FROM PACSRetrievedImages WHERE BaseUri = :baseUri");
query.bindValue(":baseUri", pacsDevice.getBaseUri());
}
else // WADO-URI + DIMSE
{
query.prepare("SELECT ID FROM PACSRetrievedImages WHERE AETitle = :aeTitle AND Address = :address AND QueryPort = :queryPort AND BaseUri = :baseUri");
query.bindValue(":aeTitle", pacsDevice.getAETitle());
query.bindValue(":address", pacsDevice.getAddress());
query.bindValue(":queryPort", pacsDevice.getQueryRetrieveServicePort());
query.bindValue(":baseUri", pacsDevice.getBaseUri());
}

if (executeQueryAndLogError(query) && query.next())
{
Expand Down
42 changes: 39 additions & 3 deletions starviewer/src/inputoutput/pacsdevicemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ Settings::SettingsListItemType pacsDeviceToSettingsListItem(const PacsDevice &pa
item["Type"] = "WADO";
item["BaseUri"] = pacsDevice.getBaseUri();
break;

case PacsDevice::Type::WadoUriDimse:
item["Type"] = "WADO-URI+DIMSE";
item["AETitle"] = pacsDevice.getAETitle();
item["PacsHostname"] = pacsDevice.getAddress();
item["QueryRetrieveServiceEnabled"] = pacsDevice.isQueryRetrieveServiceEnabled();
item["PacsPort"] = pacsDevice.getQueryRetrieveServicePort();
item["StoreServiceEnabled"] = pacsDevice.isStoreServiceEnabled();
item["StoreServicePort"] = pacsDevice.getStoreServicePort();
item["BaseUri"] = pacsDevice.getBaseUri();
}

item["Institution"] = pacsDevice.getInstitution();
Expand Down Expand Up @@ -99,6 +109,27 @@ PacsDevice settingsListItemToPacsDevice(const Settings::SettingsListItemType &it
pacsDevice.setType(PacsDevice::Type::Wado);
pacsDevice.setBaseUri(item["BaseUri"].toUrl());
}
else if (item["Type"].toString() == "WADO-URI+DIMSE")
{
pacsDevice.setType(PacsDevice::Type::WadoUriDimse);
pacsDevice.setAETitle(item["AETitle"].toString());
pacsDevice.setAddress(item["PacsHostname"].toString());
pacsDevice.setQueryRetrieveServiceEnabled(item.value("QueryRetrieveServiceEnabled", true).toBool());
pacsDevice.setQueryRetrieveServicePort(item["PacsPort"].toInt());

if (!item.contains("StoreServiceEnabled"))
{
pacsDevice.setStoreServiceEnabled(true);
pacsDevice.setStoreServicePort(item["PacsPort"].toInt());
}
else
{
pacsDevice.setStoreServiceEnabled(item["StoreServiceEnabled"].toBool());
pacsDevice.setStoreServicePort(item["StoreServicePort"].toInt());
}

pacsDevice.setBaseUri(item["BaseUri"].toUrl());
}

pacsDevice.setInstitution(item["Institution"].toString());
pacsDevice.setLocation(item["Location"].toString());
Expand Down Expand Up @@ -196,9 +227,14 @@ QList<PacsDevice> PacsDeviceManager::getPacsList(PacsFilter filter)
for (const Settings::SettingsListItemType &item : list)
{
PacsDevice pacs = settingsListItemToPacsDevice(item);
bool include = filter.testFlag(DimseWithQueryRetrieveService) && pacs.getType() == PacsDevice::Type::Dimse && pacs.isQueryRetrieveServiceEnabled();
include |= filter.testFlag(DimseWithStoreService) && pacs.getType() == PacsDevice::Type::Dimse && pacs.isStoreServiceEnabled();
include |= filter.testFlag(Wado) && pacs.getType() == PacsDevice::Type::Wado;
bool include = filter.testFlag(CanRetrieve) && (
((pacs.getType() == PacsDevice::Type::Dimse || pacs.getType() == PacsDevice::Type::WadoUriDimse) && pacs.isQueryRetrieveServiceEnabled())
|| pacs.getType() == PacsDevice::Type::Wado
);
include |= filter.testFlag(CanStore) && (
((pacs.getType() == PacsDevice::Type::Dimse || pacs.getType() == PacsDevice::Type::WadoUriDimse) && pacs.isStoreServiceEnabled())
|| pacs.getType() == PacsDevice::Type::Wado
);
include &= filter.testFlag(OnlyDefault) ? pacs.isDefault() : true;

if (include)
Expand Down
12 changes: 5 additions & 7 deletions starviewer/src/inputoutput/pacsdevicemanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,10 @@ class PacsDeviceManager {
public:
/// Flags to filter PACS by different properties.
enum PacsFilterFlag {
DimseWithQueryRetrieveService = 0x1,
DimseWithStoreService = 0x2,
Dimse = DimseWithQueryRetrieveService | DimseWithStoreService,
Wado = 0x4,
AllTypes = Dimse | Wado,
OnlyDefault = 0x8
CanRetrieve = 0x1,
CanStore = 0x2,
All = CanRetrieve | CanStore,
OnlyDefault = 0x4
};
Q_DECLARE_FLAGS(PacsFilter, PacsFilterFlag)

Expand All @@ -58,7 +56,7 @@ class PacsDeviceManager {
/// Returns a list of PACS stored in settings, filtered by the given flags.
/// \param[in] filter Combination of flags to filter the returned PACS.
/// \return Filtered list of PACS.
static QList<PacsDevice> getPacsList(PacsFilter filter = AllTypes);
static QList<PacsDevice> getPacsList(PacsFilter filter = All);

/// Retrieves and returns the PACS device stored with the given ID in settings.
/// \param[in] pacsID ID of the PACS that must be returned.
Expand Down
16 changes: 14 additions & 2 deletions starviewer/src/inputoutput/pacsdevicemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
namespace udg {

PacsDeviceModel::PacsDeviceModel(QObject *parent)
: QAbstractTableModel(parent), m_pacsFilter(PacsDeviceManager::AllTypes)
: QAbstractTableModel(parent), m_pacsFilter(PacsDeviceManager::All)
{
m_cachedPacsList = PacsDeviceManager::getPacsList(m_pacsFilter);
}
Expand Down Expand Up @@ -62,7 +62,19 @@ QVariant PacsDeviceModel::data(const QModelIndex &index, int role) const
switch (index.column())
{
case PacsId: return pacsDevice.getID();
case AeTitleOrBaseUri: return pacsDevice.getType() == PacsDevice::Type::Dimse ? pacsDevice.getAETitle() : pacsDevice.getBaseUri().toString();
case AeTitleOrBaseUri:
if (pacsDevice.getType() == PacsDevice::Type::Dimse)
{
return pacsDevice.getAETitle();
}
else if (pacsDevice.getType() == PacsDevice::Type::Wado)
{
return pacsDevice.getBaseUri().toString();
}
else // WADO-URI + DIMSE
{
return pacsDevice.getBaseUri().toString() + " | " + pacsDevice.getAETitle();
}
case Institution: return pacsDevice.getInstitution();
case Description: return pacsDevice.getDescription();
case Default:
Expand Down
Loading

0 comments on commit e4e3b8f

Please sign in to comment.