PACS connection

All other questions regarding DCMTK

Moderator: Moderator Team

Message
Author
Mitmal
Posts: 112
Joined: Mon, 2011-04-18, 19:36
Location: France

#31 Post by Mitmal »

I finally replaced by MOVEResponses OFList <RetrieveResponse*>, but now all the virtual methods in scuN.h (N means that it is the last version of the snapshot) are problematic.

For example:

Code: Select all

1>XXX.obj : error LNK2019: unresolved external symbol "public: __thiscall DcmSCUN::DcmSCUN(void)" (??0DcmSCUN@@QAE@XZ) referenced in function "public: __thiscall DcmTestSCUN::DcmTestSCUN(void)" (??0DcmTestSCUN@@QAE@XZ)

Code: Select all

1>XXX.obj : error LNK2001: unresolved external symbol "public: virtual class OFCondition __thiscall DcmSCUN::initNetwork(void)" (?initNetwork@DcmSCUN@@UAE?AVOFCondition@@XZ)

Code: Select all

1>XXX.obj : error LNK2001: unresolved external symbol "public: virtual class OFCondition __thiscall DcmSCUN::negotiateAssociation(void)" (?negotiateAssociation@DcmSCUN@@UAE?AVOFCondition@@XZ)

Code: Select all

1>XXX.obj : error LNK2001: unresolved external symbol "public: virtual class OFCondition __thiscall DcmSCUN::sendECHORequest(unsigned char)" (?sendECHORequest@DcmSCUN@@UAE?AVOFCondition@@E@Z)

Code: Select all

1>XXX.obj : error LNK2001: unresolved external symbol "public: virtual class OFCondition __thiscall DcmSCUN::sendSTORERequest(unsigned char,class OFString const &,class DcmDataset *,class DcmDataset * &,class DcmDataset * &,unsigned short &)" (?sendSTORERequest@DcmSCUN@@UAE?AVOFCondition@@EABVOFString@@PAVDcmDataset@@AAPAV4@2AAG@Z)

Code: Select all

1>XXX.obj : error LNK2001: unresolved external symbol "public: virtual class OFCondition __thiscall DcmSCUN::sendMOVERequest(unsigned char,class OFString const &,class DcmDataset *,class OFList<class RetrieveResponse *> *)" (?sendMOVERequest@DcmSCUN@@UAE?AVOFCondition@@EABVOFString@@PAVDcmDataset@@PAV?$OFList@PAVRetrieveResponse@@@@@Z)

Code: Select all

1>XXX.obj : error LNK2001: unresolved external symbol "public: virtual class OFCondition __thiscall DcmSCUN::handleMOVEResponse(unsigned char,class RetrieveResponse *,bool &)" (?handleMOVEResponse@DcmSCUN@@UAE?AVOFCondition@@EPAVRetrieveResponse@@AA_N@Z)

Code: Select all

1>XXX.obj : error LNK2001: unresolved external symbol "public: virtual class OFCondition __thiscall DcmSCUN::sendCGETRequest(unsigned char,class DcmDataset *,class OFList<class RetrieveResponse *> *)" (?sendCGETRequest@DcmSCUN@@UAE?AVOFCondition@@EPAVDcmDataset@@PAV?$OFList@PAVRetrieveResponse@@@@@Z)
I'm really bothered by this problem. I really need your help :-(
Respectueusement,
MitMal

Mitmal
Posts: 112
Joined: Mon, 2011-04-18, 19:36
Location: France

#32 Post by Mitmal »

ok, here I finally found a solution. I import the latest snapshot of dcmnet.lib IN ADDITION to the original version. (in the properties of my project of course) (Adding an N at the end of the name.)

I just been obliged to comment out the function: virtual void closeAssociation (const DcmCloseAssociationType closeType) and in scuN.h and scuN.cc

But now it works, the principal.

It remains to understand how to retrieve the images now. I use, as in the example, sendMOVERequest, but where do I specify the destination folder of images??
Respectueusement,
MitMal

Mitmal
Posts: 112
Joined: Mon, 2011-04-18, 19:36
Location: France

#33 Post by Mitmal »

So here, I have a problem when I use:

Code: Select all

result = scuN.sendMOVERequest (chaired, OFmonAet, & req, & moveResponses)
OFmonAet is my AET.

Here is the error message:

Code: Select all

Passed in year DIMS Caller illegal association
Do you understand why?
Respectueusement,
MitMal

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

#34 Post by Michael Onken »

This is really the error message?! Looks weird.

Anyway, did you start the association?

Do you know that MOVE will not send the images on a different connection? You may setup storescp to receive the images on your machine. Also have configured the destination AE title on the PACS to point to your machine and to your storage receiver's IP and port (e.g. storescp).

Michael

Mitmal
Posts: 112
Joined: Mon, 2011-04-18, 19:36
Location: France

#35 Post by Mitmal »

I do not understand, can you give me a more telling example please?
Respectueusement,
MitMal

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

#36 Post by Michael Onken »

Hi,

the code in the DcmSCU howto (showing a C-MOVE client) does not retrieve any images since this is not how C-MOVE works. C-MOVE (and the code examples) initiates transfer which the PACS starts on a separate connection, where the receiver might be your computer or any other one.

Please look into the standard (part 4 on Q/R, maybe also part 7 on C-MOVE) in order to fully understand how this works. Also, I explained it several times in the forum, you may search for it.

Best regards,
Michael

Mitmal
Posts: 112
Joined: Mon, 2011-04-18, 19:36
Location: France

#37 Post by Mitmal »

I still do not understand. Why should I use sendMOVERequest while you tell me that I should use STORESCP? Can you give me a link, or more, please?

Is not it possible to have a link explaining how to retrieve images from the PACS? The full procedure?
Is there a sample code to do this? My connection works fine, but I do not know how to use STORESCP.

thank you
Respectueusement,
MitMal

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

#38 Post by Michael Onken »

Mitmal, please read what I pointed you to; I cannot explain to you how to use a hammer when you do not try to understand the concept of nails first.

Michael

Mitmal
Posts: 112
Joined: Mon, 2011-04-18, 19:36
Location: France

#39 Post by Mitmal »

I do not understand what you want me to learn.
Here is my code:

Code: Select all

/***
Charge sur le disque les series qui sont demandées depuis la zone d'importation
 -serverIpAdress represente l'adresse IP nécéssaire à la connexion
 -serverPort represente le numero du port nécéssaire à la connexion, coté serveur
 -serverAet represente l'AET nécéssaire à la connexion
 -monIpAdress represente l'adresse IP de la machine
 -monPort represente le numero du port nécéssaire à la connexion coté client
 -monAet represente l'AET de la machine
 -listeStudyIDGet représente la liste des DCM_StudyInstanceUID des Study à charger sur le disque
 */
bool MedicTool2::getImagePacs(QString serverIpAdress, QString serverPort, QString serverAet, QString monIpAdress, QString monPort, QString monAet, QStringList listeStudyIDGet)
{
	for (int numeroStudy=0; numeroStudy<listeStudyIDGet.size();numeroStudy++) //Idéalement cette boucle devrait etre placée plus en profondeur pour ne pas redemander une connexion à chaque fois au serveur
	{
		//Récupération de l'examen courant
		QString numeroIDStudy = listeStudyIDGet[numeroStudy];
		
		QProgressDialog progressDialog (tr("Récuperation en cours :")+" "+numeroIDStudy+"\n"+tr("Veuillez patienter..."),QString(),0,100);
		progressDialog.setWindowModality(Qt::WindowModal);
		progressDialog.show();

		//Mise en place des parametre pour établire la connexion DICOM
		OFLog::configure(OFLogger::DEBUG_LOG_LEVEL);
		DcmTestSCU scu;
		DcmTestSCUN scuN;
	progressDialog.setValue(10);

		//Mise en place des parametres AET
		OFString OFmonAet = monAet;				scu.setAETitle(OFmonAet);			scuN.setAETitle(OFmonAet);
		OFString OFipAdresse = serverIpAdress;	scu.setPeerHostName(OFipAdresse);	scuN.setPeerHostName(OFipAdresse);
		Uint16 OFport = serverPort.toUInt();	scu.setPeerPort(OFport);			scuN.setPeerPort(OFport);
		OFString OFaet = serverAet;				scu.setPeerAETitle(OFaet);			scuN.setPeerAETitle(OFaet);
	progressDialog.setValue(20);

		//Utilisation du contexte de présentation FIND / MOVE dans la racine des series, pour proposer toutes les syntaxes de transfert compressées (Use presentation context for FIND/MOVE in study root, propose all uncompressed transfer syntaxes)
		OFList<OFString> ts; 
		ts.push_back(UID_LittleEndianExplicitTransferSyntax); 
		ts.push_back(UID_BigEndianExplicitTransferSyntax); 
		ts.push_back(UID_LittleEndianImplicitTransferSyntax); 
		scu.addPresentationContext(UID_FINDStudyRootQueryRetrieveInformationModel, ts); scu.addPresentationContext(UID_MOVEStudyRootQueryRetrieveInformationModel, ts); scu.addPresentationContext(UID_VerificationSOPClass, ts);
		scuN.addPresentationContext(UID_FINDStudyRootQueryRetrieveInformationModel, ts); scuN.addPresentationContext(UID_MOVEStudyRootQueryRetrieveInformationModel, ts); scuN.addPresentationContext(UID_VerificationSOPClass, ts);
	progressDialog.setValue(30);
 
		//Initialisation du reseau
		OFCondition result = scu.initNetwork();
		result = scuN.initNetwork();
		if (result.bad()) 
		{//PROBLEME DE CONNEXION
			QString add = result.text();
			QMessageBox::warning(0,"CONNEXION","TENTATIVE DE CONNEXION :\nJe suis : "+monIpAdress+":"+monPort+" ("+monAet+")\nEt je veux parler à : "+serverIpAdress+":"+serverPort+" ("+serverAet+")\n\nImpossible d'initialiser le reseau: "+add);
			return false; 
		}
	progressDialog.setValue(40);
 
		//TENTATIVE D'ASSOCIATION
		result = scu.negotiateAssociation();
		result = scuN.negotiateAssociation();
		if (result.bad()) 
		{//PROBLEME D'IDENTIFICATION
			QString add = result.text();
			QMessageBox::warning(0,"CONNEXION","TENTATIVE DE CONNEXION :\nJe suis : "+monIpAdress+":"+monPort+" ("+monAet+")\nEt je veux parler à : "+serverIpAdress+":"+serverPort+" ("+serverAet+")\n\nImpossible de negocier l'association: "+add);
			return false; 
		}
	progressDialog.setValue(50);
 
		//Voyons si le serveur est en écoute: Contruction et envoi d'une demande C-ECHO (Let's look whether the server is listening: Assemble and send C-ECHO request)
		result = scu.sendECHORequest(0);
		result = scuN.sendECHORequest(0);
		if (result.bad()) 
		{//PROBLEME, LE SERVER NE REPOND PAS
			QString add = result.text();
			QMessageBox::warning(0,"CONNEXION","TENTATIVE DE CONNEXION :\nJe suis : "+monIpAdress+":"+monPort+" ("+monAet+")\nEt je veux parler à : "+serverIpAdress+":"+serverPort+" ("+serverAet+")\n\nImpossible d'utiliser le processus E-ECHO avec le serveur: "+add);
			return false;
		}
	progressDialog.setValue(60);
 
		//Construction et envoi d'une requete C-FIND, pour trouver les examens
		FINDResponses findResponses;
		OFList<QRResponse*> OFfindResponses;
	
		DcmDataset req;
		req.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "STUDY");
	
		//CONSTRUCTION DE LA REQUETE
		//Avec les numéro ID des series selectionnées
		OFString OFNumeroIDStudy = numeroIDStudy;
		req.putAndInsertOFStringArray(DCM_StudyInstanceUID, OFNumeroIDStudy);

		T_ASC_PresentationContextID presID = findUncompressedPC(UID_FINDStudyRootQueryRetrieveInformationModel, scu);
		presID = findUncompressedPC(UID_FINDStudyRootQueryRetrieveInformationModel, scuN);
		if (presID == 0)
		{//PROBLEME avec le contexte de présentation
			QMessageBox::warning(0,"CONNEXION","TENTATIVE DE CONNEXION :\nJe suis : "+monIpAdress+":"+monPort+" ("+monAet+")\nEt je veux parler à : "+serverIpAdress+":"+serverPort+" ("+serverAet+")\n\nIl n'y a pas de contexte de présentation non compressé pour Study Root FIND");
			return false;
		}
	progressDialog.setValue(70);

		result = scu.sendFINDRequest(presID, &req, &findResponses);
		result = scuN.sendFINDRequest(presID, &req, &OFfindResponses);
		if (result.bad())
		{//PROBLEME AVEC LE RESULTAT DE LA RECHERCHE, il n'y a pas de serie disponnible
			QString add = result.text();
			QMessageBox::warning(0,"CONNEXION","TENTATIVE DE CONNEXION :\nJe suis : "+monIpAdress+":"+monPort+" ("+monAet+")\nEt je veux parler à : "+serverIpAdress+":"+serverPort+" ("+serverAet+")\n\nIl n'y a pas de series disponnibles: "+add);
			return false;
		}
		else
		{ //La connexion est opérationnelle et il y a des series à récupérer
			//QMessageBox::information(0,"CONNEXION","TENTATIVE DE CONNEXION :\nJe suis : "+monIpAdress+":"+monPort+" ("+monAet+")\nEt je veux parler à : "+serverIpAdress+":"+serverPort+" ("+serverAet+")\n\nIl y a des series disponnibles");
		}
	 progressDialog.setValue(80);

		//Construction et envoi d'une requete C-MOVE, pour tous les examens identifiés au dessus
		presID = findUncompressedPC(UID_MOVEStudyRootQueryRetrieveInformationModel, scu);
		presID = findUncompressedPC(UID_MOVEStudyRootQueryRetrieveInformationModel, scuN);
		if (presID == 0)
		{//PROBLEME avec le contexte de présentation
			QMessageBox::warning(0,"CONNEXION","TENTATIVE DE CONNEXION :\nJe suis : "+monIpAdress+":"+monPort+" ("+monAet+")\nEt je veux parler à : "+serverIpAdress+":"+serverPort+" ("+serverAet+")\n\nIl n'y a pas de contexte de présentation non compressé pour Study Root MOVE");
			return false;
		}
	progressDialog.setValue(90);

		//OFListIterator(FINDResponse*) study = findResponses.begin();
		OFListIterator(QRResponse*) study = OFfindResponses.begin();
		Uint32 studyCount = 1; 
		OFBool failed = OFFalse; 
		while (study != OFfindResponses.end() && result.good()) 
		{ 
			// Pour chaque boucle en réponse, soit chaque examen, toutes les images seront récupérées
			OFList<RetrieveResponse*> moveResponses;//MOVEResponses moveResponses; 

			if ( (*study)->m_dataset != NULL) //Il faut etre certain que ce n'est pas la derniere réponse, elle ne contient pas d'information
			{ 
				OFString studyInstanceUID;
				result = (*study)->m_dataset->findAndGetOFStringArray(DCM_StudyInstanceUID, studyInstanceUID); 
				// only try to get study if we actually have study instance uid, otherwise skip it 
				if (result.good()) 
				{
					req.putAndInsertOFStringArray(DCM_StudyInstanceUID, studyInstanceUID); 
					// fetches all images of this particular study 
					OFString OFmonAet = monAet;
					result = scuN.sendMOVERequest(presID, OFmonAet, &req, &moveResponses); 
					if (result.good()) 
					{ 
						QMessageBox::information(0,"CONNEXION","YES MA POULE");
						studyCount++; 
					}
					else
					{
						QString add = result.text();
						QMessageBox::warning(0,"Recuperation depuis noeud DICOM","A) Problème : Impossible de récupérer l'examen distant : "+add);
						return false;
					}
				}
				else
				{
					QString add = result.text();
					QMessageBox::warning(0,"Recuperation depuis noeud DICOM","B) Problème ; impossible de récupérer l'instance ID de l'examen récupéré sur le reseau : "+add);
					return false;
				}
			}
			else
			{
				QMessageBox::warning(0,"Recuperation depuis noeud DICOM","C) Problème : pionteur vers examen récupéré NULL");
				return false;
			}
			study++; 
		}

		if (result.bad()) 
		{ 
			QString add = result.text();
			QMessageBox::warning(0,"CONNEXION","TENTATIVE DE CONNEXION :\nJe suis : "+monIpAdress+":"+monPort+" ("+monAet+")\nEt je veux parler à : "+serverIpAdress+":"+serverPort+" ("+serverAet+")\n\nImpossible de récupérer toutes les séries: "+add);
			return false;
		}
	progressDialog.setValue(99);

		// Release association
		//scu.closeAssociation(DCMSCU_RELEASE_ASSOCIATION); 
		QMessageBox::warning(0,"Recuperation depuis noeud DICOM","FIN D'UNE RECUPERATION");
	progressDialog.setValue(100);
	//*/
		//Fin de l'importation d'UN examen
	}
	//Fin de l'importation de tous les examens demandés
	return true;
}
My question is simple yet how to retrieve images from PACS (for examens that I ask).
How STORESCP work? Do I need an object of type STORSCP??
Respectueusement,
MitMal

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

#40 Post by Michael Onken »

Mitmal wrote:I do not understand what you want me to learn.

My question is simple yet how to retrieve images from PACS (for examens that I ask).
Ah, I can answer that question :-) As I wrote:
Please look into the standard (part 4 on Q/R, maybe also part 7 on C-MOVE) in order to fully understand how this works. Also, I explained it several times in the forum, you may search for it.
How STORESCP work?
Google knows better (first match!) and more comprehensive than I can write in the forum.
Do I need an object of type STORSCP??
As I said, in case of C-MOVE, the PACS will open a separate connection to send files. You have to listen for that connection. One way to do this is to start the tool storescp, which is part of DCMTK.

Best regards,
Michael

Mitmal
Posts: 112
Joined: Mon, 2011-04-18, 19:36
Location: France

#41 Post by Mitmal »

ok, I understand better now.
So by running STORESCP, a console appears and I have to keep it open. I get the images directly in the directory where STORESCP.exe runs. Thing I do not want.

So I wonder if it is possible to start the listening port (STORESCP) directly from my program and if I can specify the destination folder as well?
Respectueusement,
MitMal

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

#42 Post by Michael Onken »

Hi,

not with DcmSCU, or, not without extending the code in a derived class. You could start another process or thread that uses DcmSCP to listen for images.

Michael

Mitmal
Posts: 112
Joined: Mon, 2011-04-18, 19:36
Location: France

#43 Post by Mitmal »

I can not implement the class STORESCP.cc in my program, can you tell me how to do?
Respectueusement,
MitMal

Mitmal
Posts: 112
Joined: Mon, 2011-04-18, 19:36
Location: France

#44 Post by Mitmal »

For now my only solution is to use a command. My code is:

Code: Select all

QString commande = "storescp.exe --aetitle \""+monAet+"\" -od \""+"C:/Users/ARNGDC2/Desktop/Recuperation"+"\" "+monPort;
	system(commande);

But that's not really nice, especially to the firewire confirmation which is requested each time.
In addition I should run the command in another thread because it froze my whole application, you know how you do that?
Respectueusement,
MitMal

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

#45 Post by Michael Onken »

Hi,

In order to implement it yourself, look at the DcmSCP class (dcmnet/libsrc/scp.cc), derive from it, and overwrite the function handleIncomingCommand() to handle also C-STORE (msg->CommandField == DIMSE_C_STORE_RQ) and then do something with the incoming dataset.

Michael

Locked

Who is online

Users browsing this forum: Ahrefs [Bot], Google [Bot] and 1 guest