void FindAssociation::OnAddPresentationContext(T_ASC_Parameters* params) { OFCondition cond = ASC_addPresentationContext(params, 1, m_abstractSyntax.c_str(), AllTransferSyntaxes, 3); if (cond.bad()) { LOG_ERROR(ambitolog, "Unable to add default presentation context"); } }
OFCondition PACSConnection::configureEcho() { const char *transferSyntaxes[] = { UID_LittleEndianImplicitTransferSyntax }; // PresentationContextID always has to be odd int presentationContextID = 1; return ASC_addPresentationContext(m_associationParameters, presentationContextID, UID_VerificationSOPClass, transferSyntaxes, DIM_OF(transferSyntaxes)); }
OFCondition Network::addAllStoragePresentationContexts(T_ASC_Parameters *params, bool bProposeCompression, int lossy) { OFCondition cond = EC_Normal; int i; int pid = 1; /* ** We prefer to accept Explicitly encoded transfer syntaxes. ** If we are running on a Little Endian machine we prefer ** LittleEndianExplicitTransferSyntax to BigEndianTransferSyntax. ** Some SCP implementations will just select the first transfer ** syntax they support (this is not part of the standard) so ** organise the proposed transfer syntaxes to take advantage ** of such behaviour. */ const char** transferSyntaxes; int transferSyntaxes_count; const char* const_transferSyntaxes[] = {UID_JPEGProcess14SV1TransferSyntax, NULL, NULL, UID_LittleEndianImplicitTransferSyntax }; if(bProposeCompression) { if(lossy == 8) { const_transferSyntaxes[0] = UID_JPEGProcess1TransferSyntax; } if(lossy == 12) { const_transferSyntaxes[0] = UID_JPEGProcess2_4TransferSyntax; } } /* gLocalByteOrder is defined in dcxfer.h */ if (gLocalByteOrder == EBO_LittleEndian) { /* we are on a little endian machine */ const_transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax; const_transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax; } else { /* we are on a big endian machine */ const_transferSyntaxes[1] = UID_BigEndianExplicitTransferSyntax; const_transferSyntaxes[2] = UID_LittleEndianExplicitTransferSyntax; } if(bProposeCompression) { transferSyntaxes = &const_transferSyntaxes[0]; transferSyntaxes_count = DIM_OF(const_transferSyntaxes); } else { transferSyntaxes = &const_transferSyntaxes[1]; transferSyntaxes_count = DIM_OF(const_transferSyntaxes)-1; } /* the array of Storage SOP Class UIDs comes from dcuid.h */ for (i=0; i<numberOfAllDcmStorageSOPClassUIDs && cond.good(); i++) { cond = ASC_addPresentationContext(params, pid, dcmAllStorageSOPClassUIDs[i], transferSyntaxes, transferSyntaxes_count); pid += 2; /* only odd presentation context id's */ } return cond; }
OFCondition PACSConnection::configureMove() { T_ASC_PresentationContextID associationPresentationContextID = 1; const char *transferSyntaxes[] = { NULL, NULL, NULL }; getTransferSyntaxForFindOrMoveConnection(transferSyntaxes); return ASC_addPresentationContext(m_associationParameters, associationPresentationContextID, UID_MOVEStudyRootQueryRetrieveInformationModel, transferSyntaxes, 3 /*number of TransferSyntaxes*/); }
OFCondition PACSConnection::configureFind() { const char *transferSyntaxes[] = { NULL, NULL, NULL }; // Sempre ha de ser imparell, el seu valor és 1 perquè només passem un presentation context int presentationContextID = 1; getTransferSyntaxForFindOrMoveConnection(transferSyntaxes); return ASC_addPresentationContext(m_associationParameters, presentationContextID, UID_FINDStudyRootQueryRetrieveInformationModel, transferSyntaxes, DIM_OF(transferSyntaxes)); }
OFCondition PACSConnection::addPresentationContext(int presentationContextId, const QString &abstractSyntax, QList<const char*> transferSyntaxList) { // Create an array of supported/possible transfer syntaxes const char **transferSyntaxes = new const char*[transferSyntaxList.size()]; int transferSyntaxCount = 0; foreach (const char *transferSyntax, transferSyntaxList) { transferSyntaxes[transferSyntaxCount++] = transferSyntax; } OFCondition condition = ASC_addPresentationContext(m_associationParameters, presentationContextId, qPrintable(abstractSyntax), transferSyntaxes, transferSyntaxCount, ASC_SC_ROLE_DEFAULT); delete[] transferSyntaxes; return condition; }
OFCondition Association::addAllStoragePresentationContexts(T_ASC_Parameters *params, bool /*bProposeCompression*/, int /*lossy*/) { unsigned int pid = 1; OFCondition cond = ASC_addPresentationContext(params, pid, m_abstractSyntax.c_str(), AllTransferSyntaxes, 3); if (cond.bad()) { LOG_ERROR(ambitolog, "Unable to add presentation context for " << m_abstractSyntax); return cond; } pid += 2; if (pid >= 255) { LOG_WARN(ambitolog, "Too many PresentationContexts setted"); } return cond; }
OFCondition Network::ASC_ConnectAssociation(Association* assoc, const std::string& peerTitle, const std::string& peer, int port, const std::string& ouraet, const char *abstractSyntax, int lossy) { OFCondition cond; T_ASC_Parameters *params; bool bProposeCompression = assoc->GetProposeCompression(); DIC_NODENAME peerHost; DIC_NODENAME localHost; cond = ASC_createAssociationParameters(¶ms, ASC_DEFAULTMAXPDU); if (!cond.good()) { return cond; } ASC_setAPTitles(params, ouraet.c_str(), peerTitle.c_str(), NULL); gethostname(localHost, sizeof(localHost) - 1); snprintf(peerHost, sizeof(peerHost), "%s:%d", peer.c_str(), port); ASC_setPresentationAddresses(params, localHost, peerHost); if(abstractSyntax == NULL) { cond = addAllStoragePresentationContexts(params, bProposeCompression, lossy); if (!cond.good()) { return cond; } } else { const char** transferSyntaxes; int transferSyntaxes_count; const char* const_transferSyntaxes[] = { UID_LittleEndianExplicitTransferSyntax, UID_BigEndianExplicitTransferSyntax, UID_LittleEndianImplicitTransferSyntax }; transferSyntaxes = &const_transferSyntaxes[0]; transferSyntaxes_count = DIM_OF(const_transferSyntaxes); /* const char* const_transferSyntaxes[] = { UID_JPEGProcess14SV1TransferSyntax, UID_LittleEndianExplicitTransferSyntax, UID_BigEndianExplicitTransferSyntax, UID_LittleEndianImplicitTransferSyntax }; if(bProposeCompression) { if(lossy == 8) { const_transferSyntaxes[0] = UID_JPEGProcess1TransferSyntax; } if(lossy == 12) { const_transferSyntaxes[0] = UID_JPEGProcess2_4TransferSyntax; } } const_transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax; const_transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax; if(bProposeCompression) { transferSyntaxes = &const_transferSyntaxes[0]; transferSyntaxes_count = DIM_OF(const_transferSyntaxes); } else { transferSyntaxes = &const_transferSyntaxes[1]; transferSyntaxes_count = DIM_OF(const_transferSyntaxes)-1; }*/ cond = ASC_addPresentationContext(params, 1, abstractSyntax, transferSyntaxes, transferSyntaxes_count); assoc->OnAddPresentationContext(params, transferSyntaxes, transferSyntaxes_count); } /* create association */ cond = ASC_requestAssociation(net, params, &(assoc->assoc)); if (cond != EC_Normal) { if (cond == DUL_ASSOCIATIONREJECTED) { T_ASC_RejectParameters rej; ASC_getRejectParameters(params, &rej); ASC_printRejectParameters(stderr, &rej); return cond; } else { return cond; } } /* what has been accepted/refused ? */ if (ASC_countAcceptedPresentationContexts(params) == 0) { return cond; } return EC_Normal; }
void Association ::associate(Network & network) { if(!network.is_initialized()) { throw Exception("Network is not initialized"); } if(this->is_associated()) { throw Exception("Already associated"); } OFCondition condition; T_ASC_Parameters * params; condition = ASC_createAssociationParameters(¶ms, ASC_MAXIMUMPDUSIZE); if(condition.bad()) { throw Exception(condition); } condition = ASC_setAPTitles(params, this->_own_ae_title.c_str(), this->_peer_ae_title.c_str(), NULL); if(condition.bad()) { ASC_destroyAssociationParameters(¶ms); throw Exception(condition); } std::string localhost(128, '\0'); gethostname(&localhost[0], localhost.size()-1); std::ostringstream peer; peer << this->_peer_host_name << ":" << this->_peer_port; condition = ASC_setPresentationAddresses(params, "localhost", peer.str().c_str()); if(condition.bad()) { ASC_destroyAssociationParameters(¶ms); throw Exception(condition); } unsigned int context_id = 1; for(auto const & context: this->_presentation_contexts) { char const ** transfer_syntaxes = new char const *[context.transfer_syntaxes.size()]; for(std::size_t i = 0; i < context.transfer_syntaxes.size(); ++i) { transfer_syntaxes[i] = context.transfer_syntaxes[i].c_str(); } condition = ASC_addPresentationContext(params, context_id, context.abstract_syntax.c_str(), transfer_syntaxes, context.transfer_syntaxes.size(), context.role); if(condition.bad()) { ASC_destroyAssociationParameters(¶ms); throw Exception(condition); } context_id += 2; } if(this->_user_identity_type == UserIdentityType::None) { // Nothing to do. } else if(this->_user_identity_type == UserIdentityType::Username) { condition = ASC_setIdentRQUserOnly(params, this->_user_identity_primary_field.c_str()); } else if(this->_user_identity_type == UserIdentityType::UsernameAndPassword) { condition = ASC_setIdentRQUserOnly(params, this->_user_identity_primary_field.c_str(), this->_user_identity_secondary_field.c_str()); } else if(this->_user_identity_type == UserIdentityType::Kerberos) { condition = ASC_setIdentRQKerberos(params, this->_user_identity_primary_field.c_str(), this->_user_identity_primary_field.size()); } else if(this->_user_identity_type == UserIdentityType::SAML) { condition = ASC_setIdentRQSaml(params, this->_user_identity_primary_field.c_str(), this->_user_identity_primary_field.size()); } else { ASC_destroyAssociationParameters(¶ms); throw Exception("Unknown identity type"); } if(condition.bad()) { ASC_destroyAssociationParameters(¶ms); throw Exception(condition); } condition = ASC_requestAssociation( network.get_network(), params, &this->_association); if(condition.bad()) { OFString empty; if(condition == DUL_ASSOCIATIONREJECTED) { T_ASC_RejectParameters rej; ASC_getRejectParameters(params, &rej); ASC_destroyAssociationParameters(¶ms); throw Exception(ASC_printRejectParameters(empty, &rej).c_str()); } else { ASC_destroyAssociationParameters(¶ms); throw Exception(DimseCondition::dump(empty, condition).c_str()); } } }
// TODO Estudiar si el millor transferSyntax per defecte és UID_LittleEndianExplicitTransferSyntax o com els cas del move és el JPegLossLess OFCondition PACSConnection::configureStore() { // Each SOP Class will be proposed in two presentation contexts (unless the opt_combineProposedTransferSyntaxes global variable is true). // The command line specified a preferred transfer syntax to use. This prefered transfer syntax will be proposed in one presentation context // and a set of alternative (fallback) transfer syntaxes will be proposed in a different presentation context. // Generally, we prefer to use Explicitly encoded transfer syntaxes and if running on a Little Endian machine we prefer LittleEndianExplicitTransferSyntax // to BigEndianTransferSyntax. Some SCP implementations will just select the first transfer syntax they support (this is not part of the standard) so // organise the proposed transfer syntaxes to take advantage of such behaviour. /// Indiquem que con Transfer syntax preferida volem utilitzar JpegLossless const char *preferredTransferSyntax = UID_JPEGProcess14SV1TransferSyntax; QList<const char*> fallbackSyntaxes; fallbackSyntaxes.append(UID_LittleEndianExplicitTransferSyntax); fallbackSyntaxes.append(UID_BigEndianExplicitTransferSyntax); fallbackSyntaxes.append(UID_LittleEndianImplicitTransferSyntax); // Afegim totes les classes SOP de transfarència d'imatges. com que desconeixem de quina modalitat són // les imatges alhora de preparar la connexió les hi incloem totes les modalitats. Si alhora de connectar sabèssim de quina modalitat és // l'estudi només caldria afegir-hi la de la motalitat de l'estudi // Les sopClass o també conegudes com AbstractSyntax és equivalent amb el Move quina acció volem fer per exemple // UID_MOVEStudyRootQueryRetrieveInformationModel, en el case del move, en el cas de StoreScu, el sopClass que tenim van en funció del tipus d'imatge // per exemple tenim ComputedRadiographyImageStorage, CTImageStore, etc.. aquestes sopClass indiquen quin tipus d'imatge anirem a guardar, per això sinó // sabem de quin tipus de SOPClass són les imatges que anem a guardar al PACS, li indiquem una llista per defecte que cobreix la gran majoria i més // comuns de SOPClass que existeixen // Amb l'Abstract syntax o SOPClass definim quina operació volem fer, i amb els transfer syntax indiquem amb quin protocol es farà // la transfarència de les dades, LittleEndian, JPegLossLess, etc.. // TODO Si que la podem arribar a saber la transfer syntax, només hem de mirar la SOPClassUID de cada imatge a enviar, mirar // codi storescu.cc a partir de la línia 639 QStringList sopClasses; for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs; i++) { // Comprovem que no hi hagi que cap SOPClas duplicada if (!sopClasses.contains(QString(dcmShortSCUStorageSOPClassUIDs[i]))) { sopClasses.append(QString(dcmShortSCUStorageSOPClassUIDs[i])); } } OFCondition condition = EC_Normal; int presentationContextID = 1; // Creem un presentation context per cada SOPClass que tinguem, indicant per cada SOPClass quina transfer syntax utilitzarem // En el cas del Store amb el presentation Context indiquem que per cada tipus d'imatge que volem guardar SOPClass amb quins // transfer syntax ens podem comunicar, llavors el PACS ens indicarà si ell pot guardar aquest tipus de SOPClass, i amb quin // transfer syntax li hem d'enviar la imatge foreach (const QString &sopClass, sopClasses) { // No poden haver més de 255 presentation context if (presentationContextID > 255) { return ASC_BADPRESENTATIONCONTEXTID; } // Sop class with preferred transfer syntax condition = ASC_addPresentationContext(m_associationParameters, presentationContextID, qPrintable(sopClass), &preferredTransferSyntax, 1, ASC_SC_ROLE_DEFAULT); // Only odd presentation context id's presentationContextID += 2; if (!condition.good()) { break; } if (fallbackSyntaxes.size() > 0) { if (presentationContextID > 255) { return ASC_BADPRESENTATIONCONTEXTID; } // Sop class with fallback transfer syntax condition = addPresentationContext(presentationContextID, sopClass, fallbackSyntaxes); // Only odd presentation context id's presentationContextID += 2; if (!condition.good()) { break; } } } return condition; }
void QtDcmServersDicomSettingsWidget::sendEcho() { if ( !treeWidget->currentItem() ) { return; } QtDcmPreferences* prefs = QtDcmPreferences::instance(); const QString aet = prefs->aetitle(); const QString serverAet = treeWidget->currentItem()->data ( 1, 1 ).toString(); const QString hostname = prefs->hostname(); const QString serverHostname = treeWidget->currentItem()->data ( 3, 1 ).toString(); const QString serverPort = treeWidget->currentItem()->data ( 2, 1 ).toString(); T_ASC_Network *net = 0; // network struct, contains DICOM upper layer FSM etc. OFCondition cond = ASC_initializeNetwork ( NET_REQUESTOR, 0, 30 /* timeout */, &net ); if ( cond != EC_Normal ) { QMessageBox msgBox( QApplication::activeWindow() ); msgBox.setIcon ( QMessageBox::Critical ); msgBox.setText ( "Cannot initialize network" ); msgBox.exec(); ASC_dropNetwork ( &net ); return; } QTcpSocket * socket = new QTcpSocket(this); socket->connectToHost(serverHostname, serverPort.toInt()); if (!socket->waitForConnected(1000)) { QMessageBox msgBox( QApplication::activeWindow() ); msgBox.setIcon ( QMessageBox::Information ); msgBox.setText ( "Cannot connect to server " + serverHostname + " on port " + serverPort + " !" ); msgBox.exec(); ASC_dropNetwork ( &net ); return; } socket->disconnectFromHost(); delete socket; T_ASC_Parameters *params; // parameters of association request cond = ASC_createAssociationParameters ( ¶ms, ASC_DEFAULTMAXPDU ); // set calling and called AE titles cond = ASC_setAPTitles ( params, aet.toUtf8().data(), serverAet.toUtf8().data(), NULL ); // the DICOM server accepts connections at server.nowhere.com port 104 cond = ASC_setPresentationAddresses ( params, hostname.toUtf8().data(), QString ( serverHostname + ":" + serverPort ).toLatin1().data() ); // list of transfer syntaxes, only a single entry here const char* ts[] = { UID_LittleEndianImplicitTransferSyntax }; // add presentation context to association request cond = ASC_addPresentationContext ( params, 1, UID_VerificationSOPClass, ts, 1 ); // request DICOM association T_ASC_Association *assoc = 0; if ( ASC_requestAssociation ( net, params, &assoc ).good() ) { if ( ASC_countAcceptedPresentationContexts ( params ) == 1 ) { // the remote SCP has accepted the Verification Service Class DIC_US id = assoc->nextMsgID++; // generate next message ID DIC_US status; // DIMSE status of C-ECHO-RSP will be stored here DcmDataset *sd = NULL; // status detail will be stored here // send C-ECHO-RQ and handle response DIMSE_echoUser ( assoc, id, DIMSE_BLOCKING, 0, &status, &sd ); delete sd; // we don't care about status detail QMessageBox msgBox( QApplication::activeWindow() ); msgBox.setIcon ( QMessageBox::Information ); msgBox.setText ( "Echo request successful !" ); msgBox.exec(); } else { QMessageBox msgBox( QApplication::activeWindow() ); msgBox.setIcon ( QMessageBox::Critical ); msgBox.setText ( "Wrong presentation context, echo request failed" ); msgBox.exec(); } } else { QMessageBox msgBox( QApplication::activeWindow() ); msgBox.setIcon ( QMessageBox::Critical ); msgBox.setText ( "Wrong dicom association, echo request failed" ); msgBox.exec(); } ASC_releaseAssociation ( assoc ); // release association ASC_destroyAssociation ( &assoc ); // delete assoc structure ASC_dropNetwork ( &net ); // delete net structure net = 0; assoc = 0; }
bool echoscu(const QString &peerTitle, const QString &ourTitle, const QString &hostname, int port, QString &msg) { T_ASC_Network *net; T_ASC_Parameters *params; T_ASC_Association *assoc; OFString temp_str; bool ret = false; #ifdef HAVE_WINSOCK_H WSAData winSockData; /* we need at least version 1.1 */ WORD winSockVersionNeeded = MAKEWORD( 1, 1 ); WSAStartup(winSockVersionNeeded, &winSockData); #endif /* initialize network, i.e. create an instance of T_ASC_Network*. */ OFCondition cond = ASC_initializeNetwork(NET_REQUESTOR, 0, 6, &net); if (cond.bad()) { DimseCondition::dump(temp_str, cond); msg = QString::fromLatin1(temp_str.c_str()); goto cleanup; } /* initialize asscociation parameters, i.e. create an instance of T_ASC_Parameters*. */ cond = ASC_createAssociationParameters(¶ms, ASC_DEFAULTMAXPDU); if (cond.bad()) { DimseCondition::dump(temp_str, cond); msg = QString::fromLatin1(temp_str.c_str()); goto cleanup; } ASC_setAPTitles(params, ourTitle.toLocal8Bit().data(), peerTitle.toLocal8Bit().data(), NULL); /* Set the transport layer type (type of network connection) in the params */ /* strucutre. The default is an insecure connection; where OpenSSL is */ /* available the user is able to request an encrypted,secure connection. */ cond = ASC_setTransportLayerType(params, OFFalse); if (cond.bad()) { DimseCondition::dump(temp_str, cond); msg = QString::fromLatin1(temp_str.c_str()); goto cleanup; } /* Figure out the presentation addresses and copy the */ /* corresponding values into the association parameters.*/ DIC_NODENAME localHost; DIC_NODENAME peerHost; gethostname(localHost, sizeof(localHost) - 1); sprintf(peerHost, "%s:%d", hostname.toLocal8Bit().data(), port); ASC_setPresentationAddresses(params, localHost, peerHost); /* Set the presentation contexts which will be negotiated */ /* when the network connection will be established */ int presentationContextID = 1; /* odd byte value 1, 3, 5, .. 255 */ for (unsigned long ii=0; ii<1; ii++) { cond = ASC_addPresentationContext(params, presentationContextID, UID_VerificationSOPClass, transferSyntaxes, 3); presentationContextID += 2; if (cond.bad()) { DimseCondition::dump(temp_str, cond); msg = QString::fromLatin1(temp_str.c_str()); goto cleanup; } } /* create association, i.e. try to establish a network connection to another */ /* DICOM application. This call creates an instance of T_ASC_Association*. */ cond = ASC_requestAssociation(net, params, &assoc); if (cond.bad()) { if (cond == DUL_ASSOCIATIONREJECTED) { T_ASC_RejectParameters rej; ASC_getRejectParameters(params, &rej); ASC_printRejectParameters(temp_str, &rej); msg = QString("Association Rejected: %1").arg(temp_str.c_str()); goto cleanup; } else { DimseCondition::dump(temp_str, cond); msg = QString("Association Request Failed: %1").arg(temp_str.c_str()); goto cleanup; } } /* count the presentation contexts which have been accepted by the SCP */ /* If there are none, finish the execution */ if (ASC_countAcceptedPresentationContexts(params) == 0) { msg = QString("No Acceptable Presentation Contexts"); goto cleanup; } /* do the real work, i.e. send a number of C-ECHO-RQ messages to the DICOM application */ /* this application is connected with and handle corresponding C-ECHO-RSP messages. */ DIC_US msgId = assoc->nextMsgID++; DIC_US status; DcmDataset *statusDetail = NULL; /* send C-ECHO-RQ and handle response */ cond = DIMSE_echoUser(assoc, msgId, DIMSE_BLOCKING, 0, &status, &statusDetail); /* check for status detail information, there should never be any */ if (statusDetail != NULL) { delete statusDetail; } /* tear down association, i.e. terminate network connection to SCP */ if (cond == EC_Normal) { cond = ASC_releaseAssociation(assoc); ret = true; } else if (cond == DUL_PEERABORTEDASSOCIATION) { } else { DimseCondition::dump(temp_str, cond); msg = QString::fromLatin1(temp_str.c_str()); cond = ASC_abortAssociation(assoc); } cleanup: /* destroy the association, i.e. free memory of T_ASC_Association* structure. This */ /* call is the counterpart of ASC_requestAssociation(...) which was called above. */ cond = ASC_destroyAssociation(&assoc); /* drop the network, i.e. free memory of T_ASC_Network* structure. This call */ /* is the counterpart of ASC_initializeNetwork(...) which was called above. */ cond = ASC_dropNetwork(&net); #ifdef HAVE_WINSOCK_H WSACleanup(); #endif return ret; }