Add C-Store protocol.

This commit is contained in:
sunwen
2022-10-27 11:03:25 +08:00
parent 6e1dfb868e
commit 7c8d97c73d
12 changed files with 436 additions and 8 deletions

View File

@@ -1,8 +1,8 @@
#include "dicomviewerhelper.h"
#include <qDebug>
#include <QFile>
#include <QDir>
#include <QApplication>
#include <QDebug>
//----------------------------------------------------------------------------------

View File

@@ -8,7 +8,8 @@
#include <QJsonArray>
struct host {
struct host
{
QString name;
QString ae;
QString ip;

View File

@@ -49,7 +49,6 @@ bool GetWorker::initDcmSCU()
void GetWorker::getBySeriesUID(const QString& studyInstanceUID, const QString& seriesInstanceUID, const QString& aDicomType)
{
qDebug(aDicomType.toLatin1());
if (!initDcmSCU())
{
mScu->abortAssociation();

View File

@@ -0,0 +1,89 @@
#include "StoreWorker.h"
#include "dcmtk/dcmnet/scu.h"
StoreWorker::StoreWorker(QObject* aParent)
: QObject(aParent)
{
}
bool StoreWorker::initDcmSCU()
{
mScu = new DcmSCU();
mScu->setAETitle(mOurTitle);
mScu->setPeerHostName(mPeerIP);
mScu->setPeerPort(OFstatic_cast(Uint16, mPeerPort));
mScu->setPeerAETitle(mPeerTitle);
OFList<OFString> syntaxes;
syntaxes.push_back(UID_LittleEndianExplicitTransferSyntax);
mScu->addPresentationContext(UID_CTImageStorage, syntaxes);
mScu->addPresentationContext(UID_MRImageStorage, syntaxes);
OFCondition cond = mScu->initNetwork();
if (cond.bad())
{
return false;
}
cond = mScu->negotiateAssociation();
if (cond.bad())
{
return false;
}
return true;
}
void StoreWorker::setPacsInfo(const std::string& aPeerIP, unsigned short aPeerPort, const std::string& aPeerTitle, const std::string& aOurTitle)
{
mPeerIP = aPeerIP;
mPeerPort = aPeerPort;
mPeerTitle = aPeerTitle;
mOurTitle = aOurTitle;
}
void StoreWorker::cancel()
{
mCancelFlag = true;
}
void StoreWorker::store(const QStringList& aFilePathList, const QString& aDicomType)
{
if (!initDcmSCU())
{
mScu->abortAssociation();
emit notifyStoreDone(0);
emit notifyStoreFinished();
delete mScu;
mScu = nullptr;
return;
}
T_ASC_PresentationContextID pcid;
Uint16 response;
DcmDataset dataset;
for(int i = 0;i < aFilePathList.size(); ++i)
{
if(mCancelFlag)
{
mCancelFlag = false;
break;
}
if(aDicomType.toLower() == "mr")
{
pcid = mScu->findPresentationContextID(UID_MRImageStorage,"");
}
else
{
pcid = mScu->findPresentationContextID(UID_CTImageStorage,"");
}
OFCondition cond = mScu->sendSTORERequest(pcid,aFilePathList.at(i).toStdString(), &dataset, response);
if (cond.bad() || cond != EC_Normal || response != 0)
{
mScu->closeAssociation(DCMSCU_ABORT_ASSOCIATION);
emit notifyStoreDone(0);
emit notifyStoreFinished();
return;
}
emit notifyStoreDone(i+1);
}
mScu->releaseAssociation();
delete mScu;
mScu = nullptr;
emit notifyStoreFinished();
}

View File

@@ -0,0 +1,35 @@
#ifndef STOREWORKER_H
#define STOREWORKER_H
#include <QObject>
class DcmSCU;
class StoreWorker : public QObject
{
Q_OBJECT
public:
StoreWorker(QObject* aParent);
void setPacsInfo(const std::string& aPeerIP, unsigned short aPeerPort, const std::string& aPeerTitle, const std::string& aOurTitle);
void cancel();
Q_INVOKABLE void store(const QStringList& aFilePathList, const QString& aDicomType);
signals:
void notifyStoreDone(int aIndex); //0 failed,1,2,3.. sucessed
void notifyStoreFinished();
private:
bool initDcmSCU();
private:
DcmSCU* mScu;
QString mStoreFilePath;
std::string mPeerTitle;
std::string mPeerIP;
std::string mOurTitle;
unsigned short mPeerPort;
unsigned short mOurPort;
bool mCancelFlag = false;
};
#endif // STOREWORKER_H

View File

@@ -0,0 +1,57 @@
#include "StoreDialog.h"
#include <QProgressBar>
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QSpacerItem>
namespace
{
const QString SENDING_IMAGES_TO_PACS = "Sending images to PACS...";
QString makeText(int aTotalNum, int aCurrentNum)
{
return SENDING_IMAGES_TO_PACS + "[" + QString::number(aCurrentNum) + "/" + QString::number(aTotalNum) + "]";
}
}
StoreDialog::StoreDialog(QWidget* aParent)
: QDialog(aParent)
, mFileSize(0)
, mProgressBar(new QProgressBar(this))
, mText(new QLabel(this))
, mCancelButton(nullptr)
{
setFixedSize(QSize(380,105));
initWidget();
}
void StoreDialog::initWidget()
{
mProgressBar->setTextVisible(false);
QVBoxLayout* vLayout = new QVBoxLayout(this);
vLayout->addWidget(mProgressBar);
vLayout->addWidget(mText);
QWidget* buttonContainer = new QWidget(this);
vLayout->addWidget(buttonContainer);
QHBoxLayout* hLayout = new QHBoxLayout(buttonContainer);
hLayout->addSpacerItem(new QSpacerItem(280,105));
mCancelButton = new QPushButton(tr("Cancel"),buttonContainer);
mCancelButton->setFixedHeight(23);
hLayout->addWidget(mCancelButton);
connect(mCancelButton,&QPushButton::clicked,this,&QDialog::reject);
}
void StoreDialog::setFileSize(int aFileSize)
{
mFileSize = aFileSize;
mText->setText(makeText(mFileSize, 0));
}
void StoreDialog::setStoreProgress(int aNum)
{
mProgressBar->setValue((aNum*100)/mFileSize);
mText->setText(makeText(mFileSize, aNum));
}

View File

@@ -0,0 +1,27 @@
#ifndef STOREDIALOG_H
#define STOREDIALOG_H
#include <QDialog>
class QProgressBar;
class QPushButton;
class QLabel;
class StoreDialog : public QDialog
{
public:
StoreDialog(QWidget* aParent = nullptr);
void setStoreProgress(int aNum);
void setFileSize(int aFileSize);
private:
void initWidget();
private:
int mFileSize;
QProgressBar* mProgressBar;
QLabel* mText;
QPushButton* mCancelButton;
};
#endif // STOREDIALOG_H

View File

@@ -8,8 +8,9 @@
#include <QButtonGroup>
#include <QActionGroup>
#include "Rendering/Measure/Measure.h"
#include "Common/QGlobals.h"
#include "ExportToolButton.h"
typedef tuple<const char *, const char *, int> ActionProperty;
namespace {
const char *SYNC_MANUAL_URL = ":/InfiniteViewer/Icon/sync/sync_manual.png";
@@ -30,7 +31,7 @@ namespace {
DefaultToolBar::DefaultToolBar(QWidget *parent) : QToolBar(parent)
, mBtnFile(new QToolButton(this))
, mBtnImport(new QToolButton(this))
, mBtnSave(new QToolButton(this))
, mBtnSave(new ExportToolButton(this))
, mBtnGrid(new QToolButton(this))
, mBtnSync(new QToolButton(this))
, mBtnAnonymize(new QToolButton(this))
@@ -52,7 +53,8 @@ DefaultToolBar::DefaultToolBar(QWidget *parent) : QToolBar(parent)
, mActionMinimize(nullptr)
, mActionMaximize(nullptr)
, mActionClose(nullptr)
, mActionFullScreen(nullptr) {
, mActionFullScreen(nullptr)
{
//init icons
mManualIcon.addFile(SYNC_MANUAL_URL);
mAutoIcon.addFile(SYNC_AUTO_URL);
@@ -64,6 +66,12 @@ DefaultToolBar::~DefaultToolBar() {
}
void DefaultToolBar::setViewManager(ImageViewManager *aManager)
{
mImageViewManager = aManager;
mBtnSave->setImageViewManager(mImageViewManager);
}
QAction* DefaultToolBar::addButton(QToolButton* button, const char* objectName) {
button->setObjectName(objectName);
button->setToolButtonStyle(Qt::ToolButtonIconOnly);
@@ -164,6 +172,7 @@ void DefaultToolBar::initImportButton() {
}
void DefaultToolBar::initExportButton() {
mBtnSave->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
mBtnSave->setToolTip(QString("Export images"));
connect(mBtnSave, &QToolButton::clicked, this, &DefaultToolBar::save);
}

View File

@@ -11,6 +11,7 @@
#include "UI/Manager/ImageViewStateCheckWorker.h"
class QButtonGroup;
class ExportToolButton;
class DefaultToolBar : public QToolBar {
Q_OBJECT
@@ -21,6 +22,7 @@ public:
void resetNeedCheckFunctionButtons();
void updateNeedCheckFunctionButtons(ViewFunctionState state);
void setViewManager(ImageViewManager* aManager);
signals:
void openFile();
@@ -70,7 +72,7 @@ private:
QIcon mDisIcon;
QToolButton *mBtnFile;
QToolButton *mBtnImport;
QToolButton *mBtnSave;
ExportToolButton *mBtnSave;
QToolButton *mBtnGrid;
QToolButton *mBtnSync;
QToolButton *mBtnAnonymize;
@@ -99,6 +101,7 @@ private:
QAction *mActionHideMeasure;
QAction *mActionHidePatData;
QAction* mSyncActions[3]={nullptr,nullptr,nullptr};
ImageViewManager* mImageViewManager;
};

View File

@@ -0,0 +1,179 @@
#include "ExportToolButton.h"
#include "Common/ImageSetStore.h"
#include "PACS/Common/dicomviewerhelper.h"
#include "PACS/Network/StoreWorker.h"
#include "PACS/Widget/StoreDialog.h"
#include "UI/Manager/ImageViewManager.h"
#include <QMenu>
namespace
{
void createFilePathListFromAllOpenedStudies(QStringList& aResult,ExtendMedicalImageProperties* aProperty)
{
if (aProperty == nullptr)
{
return;
}
std::vector<std::pair<std::string,long>> fileNames = aProperty->GetFileNames();
auto itr1 = fileNames.begin();
for(; itr1 != fileNames.end();++itr1)
{
aResult.push_back(itr1->first.c_str());
}
}
void createFilePathListFromCurrentStudy(QStringList& aResult,ExtendMedicalImageProperties* aProperty, const QString& aCurrentStudyUid)
{
if (aProperty == nullptr || aCurrentStudyUid != aProperty->GetStudyUID())
{
return;
}
std::vector<std::pair<std::string,long>> fileNames = aProperty->GetFileNames();
auto itr1 = fileNames.begin();
for(; itr1 != fileNames.end();++itr1)
{
aResult.push_back(itr1->first.c_str());
}
}
void createFilePathListFromCurrentSeries(QStringList& aResult,ExtendMedicalImageProperties* aProperty, const QString& aCurrentSeriesUid)
{
if (aProperty == nullptr || aCurrentSeriesUid != aProperty->GetSeriesUID())
{
return;
}
std::vector<std::pair<std::string,long>> fileNames = aProperty->GetFileNames();
auto itr1 = fileNames.begin();
for(; itr1 != fileNames.end();++itr1)
{
aResult.push_back(itr1->first.c_str());
}
}
}
ExportToolButton::ExportToolButton(QWidget* aParent)
: QToolButton(aParent)
, mImageViewManager(nullptr)
, mStoreDialog(nullptr)
, mStoreWorker(nullptr)
, mThread(nullptr)
{
initSendToPacsMenu();
}
void ExportToolButton::initSendToPacsMenu()
{
QMenu* saveMenu = new QMenu(this);
QMenu* sendPacs = new QMenu(tr("Send to PACS"), this);
QMenu* allOpenedStudies = new QMenu(tr("All opened studies"), sendPacs);
QMenu* currentStudy = new QMenu(tr("Current study"), sendPacs);
QMenu* currentSeries = new QMenu(tr("Current series"), sendPacs);
QList<host> hosts;
DicomViewerHelper::pacsInfo(hosts);
foreach(auto h,hosts)
{
QString text = h.name + " [" + h.ae + "@" + h.ip + ":" + h.port + "]";
auto sendToPacs = [h,this]()
{
QAction* action = qobject_cast<QAction*>(sender());
if (action == nullptr)
{
return;
}
std::vector<ExtendMedicalImageProperties*> openedFile = ImageSetStore::GetInstance()->getProperties();
if (mImageViewManager == nullptr)
{
return;
}
DicomImageView* imageView = mImageViewManager->getCurrentView();
if(imageView == nullptr)
{
return;
}
SeriesImageSet* imageSet = imageView->getSeriesInstance();
if(imageSet == nullptr)
{
return;
}
auto itr = openedFile.begin();
QStringList filePathList;
for(;itr != openedFile.end();++itr)
{
std::vector<std::pair<std::string,long>> fileNames = (*itr)->GetFileNames();
if(action->data().toString() == "All opened studies")
{
createFilePathListFromAllOpenedStudies(filePathList, (*itr));
}
else if(action->data().toString() == "Current study")
{
createFilePathListFromCurrentStudy(filePathList, (*itr), imageSet->getStudyUID());
}
else if(action->data().toString() == "Current series")
{
createFilePathListFromCurrentSeries(filePathList, (*itr), imageSet->getSeriesName());
}
}
if(filePathList.isEmpty())
{
return;
}
if (mThread == nullptr)
{
mThread = new QThread(this);
}
if (mStoreWorker == nullptr)
{
mStoreWorker = QSharedPointer<StoreWorker>(new StoreWorker(nullptr));
mStoreWorker->moveToThread(mThread);
connect(mStoreWorker.data(),&StoreWorker::notifyStoreFinished,mThread,&QThread::quit);
}
if(mStoreDialog == nullptr)
{
mStoreDialog = new StoreDialog(this);
connect(mStoreDialog,&QDialog::rejected,this,[this]()
{
mStoreWorker->cancel();
});
connect(mStoreWorker.data(),&StoreWorker::notifyStoreDone,mStoreDialog,&StoreDialog::setStoreProgress);
connect(mStoreWorker.data(),&StoreWorker::notifyStoreFinished,mStoreDialog,&StoreDialog::accept);
}
mThread->start();
mStoreWorker->setPacsInfo(h.ip.toStdString(), static_cast<unsigned short>(h.port.toShort()),
h.ae.toStdString(), DicomViewerHelper::ourTitle().toStdString());
QMetaObject::invokeMethod(mStoreWorker.data(), "store", Qt::QueuedConnection, Q_ARG(QStringList, filePathList), Q_ARG(QString, ""));
mStoreDialog->setFileSize(filePathList.size());
mStoreDialog->exec();
};
QAction* allAction = new QAction(text,allOpenedStudies);
allAction->setData("All opened studies");
connect(allAction,&QAction::triggered,this,sendToPacs);
allOpenedStudies->addAction(allAction);
QAction* studyAction = new QAction(text,currentStudy);
studyAction->setData("Current study");
connect(studyAction,&QAction::triggered,this,sendToPacs);
currentStudy->addAction(studyAction);
QAction* seriesAction = new QAction(text,currentSeries);
seriesAction->setData("Current series");
connect(seriesAction,&QAction::triggered,this,sendToPacs);
currentSeries->addAction(seriesAction);
}
saveMenu->addMenu(sendPacs);
sendPacs->addMenu(allOpenedStudies);
sendPacs->addMenu(currentStudy);
sendPacs->addMenu(currentSeries);
setPopupMode(QToolButton::MenuButtonPopup);
setMenu(saveMenu);
}
void ExportToolButton::setImageViewManager(ImageViewManager* aImageViewManager)
{
mImageViewManager = aImageViewManager;
}

View File

@@ -0,0 +1,28 @@
#ifndef EXPORTTOOLBUTTON_H
#define EXPORTTOOLBUTTON_H
#include <QToolButton>
#include <QSharedPointer>
class ImageViewManager;
class StoreDialog;
class StoreWorker;
class QThread;
class ExportToolButton : public QToolButton
{
public:
ExportToolButton(QWidget* aParent);
void setImageViewManager(ImageViewManager* aImageViewManager);
private:
void initSendToPacsMenu();
private:
ImageViewManager* mImageViewManager;
StoreDialog* mStoreDialog;
QSharedPointer<StoreWorker> mStoreWorker;
QThread* mThread;
};
#endif // EXPORTTOOLBUTTON_H

View File

@@ -1,4 +1,4 @@
#include "QDicomViewer.h"
#include "QDicomViewer.h"
#include <QFileDialog>
#include <QDebug>
@@ -23,6 +23,7 @@ QDicomViewer::QDicomViewer(QWidget *parent) : QMainWindow(parent),
m_import(nullptr) {
ui->setupUi(this);
this->statusBar()->showMessage(tr("Ready"));
ui->toolBar->setViewManager(ui->viewContainer->getViewManager());
QWidget::setWindowTitle(Project_NAME);
this->Initial();