Example #1
0
 @Override
 protected Attributes create(Association as, Attributes rq, Attributes rqAttrs, Attributes rsp)
     throws DicomServiceException {
   String localAET = as.getLocalAET();
   String sourceAET = as.getRemoteAET();
   String iuid = rq.getString(Tag.AffectedSOPInstanceUID);
   ApplicationEntity ae = as.getApplicationEntity();
   ArchiveAEExtension aeExt = ae.getAEExtension(ArchiveAEExtension.class);
   try {
     try {
       ApplicationEntity sourceAE = Archive.getInstance().findApplicationEntity(sourceAET);
       Supplements.supplementMPPS(rqAttrs, sourceAE.getDevice());
     } catch (ConfigurationNotFoundException e) {
     }
     mppsService.createPerformedProcedureStep(iuid, rqAttrs, StoreParam.valueOf(ae));
   } catch (DicomServiceException e) {
     throw e;
   } catch (Exception e) {
     throw new DicomServiceException(Status.ProcessingFailure, e);
   }
   for (String remoteAET : aeExt.getForwardMPPSDestinations())
     if (matchIssuerOfPatientID(remoteAET, rqAttrs))
       Archive.getInstance().scheduleMPPSCreate(localAET, remoteAET, iuid, rqAttrs);
   return null;
 }
Example #2
0
 @Override
 protected Attributes set(Association as, Attributes rq, Attributes rqAttrs, Attributes rsp)
     throws DicomServiceException {
   String localAET = as.getLocalAET();
   String iuid = rq.getString(Tag.RequestedSOPInstanceUID);
   ApplicationEntity ae = as.getApplicationEntity();
   ArchiveAEExtension aeExt = ae.getAEExtension(ArchiveAEExtension.class);
   PPSWithIAN ppsWithIAN;
   try {
     ppsWithIAN = mppsService.updatePerformedProcedureStep(iuid, rqAttrs, StoreParam.valueOf(ae));
   } catch (DicomServiceException e) {
     throw e;
   } catch (Exception e) {
     throw new DicomServiceException(Status.ProcessingFailure, e);
   }
   for (String remoteAET : aeExt.getForwardMPPSDestinations())
     if (matchIssuerOfPatientID(remoteAET, ppsWithIAN.pps.getPatient().getAttributes()))
       Archive.getInstance().scheduleMPPSSet(localAET, remoteAET, iuid, rqAttrs);
   List<Attributes> ians = ppsWithIAN.ians;
   Archive r = Archive.getInstance();
   if (ians != null && !ians.isEmpty())
     for (String remoteAET1 : ae.getAEExtension(ArchiveAEExtension.class).getIANDestinations())
       for (Attributes ian : ians) r.scheduleIAN(ae.getAETitle(), remoteAET1, ian);
   return null;
 }
 private void process(ObjectMessage msg) throws JMSException {
   String remoteAET = msg.getStringProperty("RemoteAET");
   String localAET = msg.getStringProperty("LocalAET");
   String iuid = msg.getStringProperty("SOPInstancesUID");
   boolean ncreate = msg.getBooleanProperty("N_CREATE_RQ");
   int retries = msg.getIntProperty("Retries");
   Attributes rqAttrs = (Attributes) msg.getObject();
   ArchiveApplicationEntity localAE =
       (ArchiveApplicationEntity) device.getApplicationEntity(localAET);
   if (localAE == null) {
     LOG.warn("Failed to forward MPPS to {} - no such local AE: {}", remoteAET, localAET);
     return;
   }
   TransferCapability tc =
       localAE.getTransferCapabilityFor(
           UID.ModalityPerformedProcedureStepSOPClass, TransferCapability.Role.SCU);
   if (tc == null) {
     LOG.warn(
         "Failed to forward MPPS to {} - local AE: {} does not support Modality Performed Procedure Step SOP Class in SCU Role",
         remoteAET,
         localAET);
     return;
   }
   AAssociateRQ aarq = new AAssociateRQ();
   aarq.addPresentationContext(
       new PresentationContext(
           1, UID.ModalityPerformedProcedureStepSOPClass, tc.getTransferSyntaxes()));
   try {
     ApplicationEntity remoteAE = aeCache.findApplicationEntity(remoteAET);
     Association as = localAE.connect(remoteAE, aarq);
     DimseRSP rsp =
         ncreate
             ? as.ncreate(UID.ModalityPerformedProcedureStepSOPClass, iuid, rqAttrs, null)
             : as.nset(UID.ModalityPerformedProcedureStepSOPClass, iuid, rqAttrs, null);
     rsp.next();
     try {
       as.release();
     } catch (IOException e) {
       LOG.info("{}: Failed to release association to {}", as, remoteAET);
     }
     if (!ncreate && rsp.getCommand().getInt(Tag.Status, -1) == Status.NoSuchObjectInstance) {
       throw new DicomServiceException(Status.NoSuchObjectInstance);
     }
   } catch (Exception e) {
     if (retries < localAE.getForwardMPPSMaxRetries()) {
       int delay = localAE.getForwardMPPSRetryInterval();
       LOG.info("Failed to forward MPPS to " + remoteAET + " - retry in " + delay + "s", e);
       scheduleForwardMPPS(localAET, remoteAET, iuid, rqAttrs, ncreate, retries + 1, delay);
     } else {
       LOG.warn("Failed to forward MPPS to " + remoteAET, e);
     }
   }
 }
 private void process(ObjectMessage msg) throws JMSException {
   String remoteAET = msg.getStringProperty("RemoteAET");
   String localAET = msg.getStringProperty("LocalAET");
   int retries = msg.getIntProperty("Retries");
   Attributes ian = (Attributes) msg.getObject();
   ArchiveApplicationEntity localAE =
       (ArchiveApplicationEntity) device.getApplicationEntity(localAET);
   if (localAE == null) {
     LOG.warn("Failed to send IAN to {} - no such local AE: {}", remoteAET, localAET);
     return;
   }
   TransferCapability tc =
       localAE.getTransferCapabilityFor(
           UID.InstanceAvailabilityNotificationSOPClass, TransferCapability.Role.SCU);
   if (tc == null) {
     LOG.warn(
         "Failed to send IAN to {} - local AE: {} does not support Instance Availability Notification SOP Class in SCU Role",
         remoteAET,
         localAET);
     return;
   }
   AAssociateRQ aarq = new AAssociateRQ();
   aarq.addPresentationContext(
       new PresentationContext(
           1, UID.InstanceAvailabilityNotificationSOPClass, tc.getTransferSyntaxes()));
   try {
     ApplicationEntity remoteAE = aeCache.findApplicationEntity(remoteAET);
     Association as = localAE.connect(remoteAE, aarq);
     DimseRSP rsp =
         as.ncreate(UID.InstanceAvailabilityNotificationSOPClass, UIDUtils.createUID(), ian, null);
     rsp.next();
     try {
       as.release();
     } catch (IOException e) {
       LOG.info("{}: Failed to release association to {}", as, remoteAET);
     }
   } catch (Exception e) {
     if (retries < localAE.getIANMaxRetries()) {
       int delay = localAE.getIANRetryInterval();
       LOG.info("Failed to send IAN to " + remoteAET + " - retry in " + delay + "s", e);
       scheduleIAN(localAET, remoteAET, ian, retries + 1, delay);
     } else {
       LOG.warn("Failed to send IAN to " + remoteAET, e);
     }
   }
 }
  /**
   * Starts a active association to a communication partner. See PS 3.8 - 7.1 A-ASSOCIATE SERVICE
   *
   * @return true, if association was successful established.
   * @exception ConnectException
   * @exception IOException
   * @exception GeneralSecurityException
   */
  public boolean aASSOCIATE() throws ConnectException, IOException, GeneralSecurityException {

    // No association may be active
    if (assoc != null) {
      throw new ConnectException("Association already established");
    }

    // New Association object for establishing an active association
    assoc = aFact.newRequestor(newSocket(url.getHost(), url.getPort()));

    // >>>> Fill the Association object with relevant data
    assoc.setAcTimeout(acTimeout);
    assoc.setDimseTimeout(dimseTimeout);
    assoc.setSoCloseDelay(soCloseDelay);
    assoc.setPackPDVs(packPDVs);

    // 1. Create an communication channel to the communication partner defined in the Association
    // object
    // 2. Send the A-ASSOCIATE-RQ package
    // 3. Receive the aAssociation acknowlage/reject package from the communication partner as a PDU
    // (Protocol Data Unit)
    PDU assocAC = assoc.connect(assocRQ);

    if (!(assocAC instanceof AAssociateAC)) {
      // Acknowlage is A-ASSOCIATE-RJ
      // Association rejected
      assoc = null;

      // Return aASSOCIATE faild
      return false;
    }
    // Acknowlage is A-ASSOCIATE_AC
    // Association accepted

    // Start the accepted association
    // API doc: AssociationFactory.newActiveAssociation(Association assoc, DcmServiceRegistry
    // services)
    aassoc = aFact.newActiveAssociation(assoc, null);
    aassoc.start();

    // Return successfull opened
    return true;
  }
Example #6
0
 void sendPPS(boolean create, Dataset pps, String aet) throws Exception {
   ActiveAssociation aa = openAssociation(aet, UIDs.GeneralPurposePerformedProcedureStepSOPClass);
   try {
     Association a = aa.getAssociation();
     DcmObjectFactory dof = DcmObjectFactory.getInstance();
     Command cmdRq = dof.newCommand();
     final String iuid = pps.getString(Tags.SOPInstanceUID);
     if (create) {
       cmdRq.initNCreateRQ(a.nextMsgID(), UIDs.GeneralPurposePerformedProcedureStepSOPClass, iuid);
     } else {
       cmdRq.initNSetRQ(a.nextMsgID(), UIDs.GeneralPurposePerformedProcedureStepSOPClass, iuid);
     }
     Dimse dimseRq =
         AssociationFactory.getInstance().newDimse(PCID_GPPPS, cmdRq, pps.exclude(SOP_IUID));
     if (log.isDebugEnabled()) {
       log.debug("GP-PPS Attributes:");
       log.debug(pps);
     }
     final Dimse dimseRsp = aa.invoke(dimseRq).get();
     final Command cmdRsp = dimseRsp.getCommand();
     final int status = cmdRsp.getStatus();
     switch (status) {
       case 0x0000:
         break;
       case 0x0116:
         log.warn(
             "Received Warning Status 116H (=Attribute Value Out of Range) from remote AE " + aet);
         break;
       default:
         throw new DcmServiceException(status, cmdRsp.getString(Tags.ErrorComment));
     }
   } finally {
     try {
       aa.release(true);
     } catch (Exception e) {
       log.warn("Failed to release " + aa.getAssociation());
     }
   }
 }
Example #7
0
 @Override
 protected Dataset doNAction(ActiveAssociation assoc, Dimse rq, Command rspCmd)
     throws IOException, DcmServiceException {
   Association a = assoc.getAssociation();
   String calledAET = a.getCalledAET();
   String callingAET = a.getCallingAET();
   String receivingAET;
   Command rqCmd = rq.getCommand();
   Dataset rqData = rq.getDataset();
   int actionTypeID = rqCmd.getInt(Tags.ActionTypeID, 0);
   rspCmd.putUS(Tags.ActionTypeID, actionTypeID);
   String iuid = rqCmd.getRequestedSOPInstanceUID();
   String tuid;
   int state;
   boolean dellock;
   switch (actionTypeID) {
     case 1:
       if (abstractSyntaxEquals(assoc, rq, UIDs.UnifiedProcedureStepPullSOPClass)) {
         type1(rqData, Tags.UPSState);
         state = UPSScpService.upsStateAsInt(rqData.getString(Tags.UPSState));
         if (state == UPSState.SCHEDULED)
           throw new DcmServiceException(MAY_ONLY_BECOME_SCHEDULED_VIA_NCREATE);
         tuid = rqData.getString(Tags.TransactionUID);
         if (tuid == null) throw new DcmServiceException(CORRECT_TRANSACTION_UID_NOT_PROVIDED);
         service.changeUPSState(calledAET, iuid, state, tuid);
         return rqData;
       }
       break;
     case 2:
       if (abstractSyntaxEquals(
           assoc,
           rq,
           UIDs.UnifiedProcedureStepPushSOPClass,
           UIDs.UnifiedProcedureStepWatchSOPClass)) {
         checkCodeItem(rqData, ItemCount.SINGLE, Tags.UPSDiscontinuationReasonCodeSeq);
         service.requestUPSCancel(calledAET, iuid, callingAET, rqData);
         return rqData;
       }
       break;
     case 3:
       if (abstractSyntaxEquals(assoc, rq, UIDs.UnifiedProcedureStepWatchSOPClass)) {
         type1(rqData, Tags.ReceivingAE);
         type1(rqData, Tags.DeletionLock);
         receivingAET = rqData.getString(Tags.ReceivingAE);
         dellock = deletionLockAsBoolean(rqData.getString(Tags.DeletionLock));
         if (iuid.equals(UIDs.UnifiedWorklistandProcedureStepSOPInstance))
           service.subscribeGlobally(calledAET, receivingAET, dellock);
         else service.subscribeReceiveUPSEventReports(calledAET, iuid, receivingAET, dellock);
         return rqData;
       }
       break;
     case 4:
       if (abstractSyntaxEquals(assoc, rq, UIDs.UnifiedProcedureStepWatchSOPClass)) {
         type1(rqData, Tags.ReceivingAE);
         receivingAET = rqData.getString(Tags.ReceivingAE);
         if (iuid.equals(UIDs.UnifiedWorklistandProcedureStepSOPInstance))
           service.unsubscribeGlobally(receivingAET);
         else service.unsubscribeReceiveUPSEventReports(iuid, receivingAET);
         return rqData;
       }
       break;
     case 5:
       if (abstractSyntaxEquals(assoc, rq, UIDs.UnifiedProcedureStepWatchSOPClass)
           && iuid.equals(UIDs.UnifiedWorklistandProcedureStepSOPInstance)) {
         type1(rqData, Tags.ReceivingAE);
         receivingAET = rqData.getString(Tags.ReceivingAE);
         service.suspendGlobalSubscription(receivingAET);
         return rqData;
       }
       break;
   }
   throw new DcmServiceException(Status.NoSuchActionType).setActionTypeID(actionTypeID);
 }
  /**
   * Queries the archive for DICOM objects matching Attribute Keys defined in the loacal field
   * "keys". This field is set by the constructor out of the configuration parameters or by the
   * methods setQueryKeys(Configuration) and setQueryKeys(Dataset). See PS 3.4 - Annex C
   * QUERY/RETRIEVE SERVICE CLASS.
   *
   * <p>The method returns, when the result is received from the communication partner.
   *
   * @return the result of the cFIND as a Vector of Dataset objects each specifying one matching
   *     DICOM object. If no matching objects are found an empty Vector is returned.
   * @throws ConnectException
   * @throws IOException
   */
  public Vector cFIND() throws ConnectException, IOException, InterruptedException {

    List dimseList;
    Vector datasetVector;

    // An association must be active
    if (aassoc == null) {
      throw new ConnectException("No Association established");
    }

    // Test, if Presentation Context for C-FIND is supported
    // API doc: Association.getAcceptedPresContext(String asuid, String tsuid)
    //        UIDs.StudyRootQueryRetrieveInformationModelGET
    if ((pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelFIND,
                        UIDs.ExplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelFIND,
                        UIDs.ImplicitVRLittleEndian))
            == null) {
      throw new ConnectException(
          "Association does not support presentation context for StudyRootQueryRetrieveInformationModelFIND SOP.");
    }

    // New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command Set Structure
    Command rqCmd = dof.newCommand();
    // API doc: Command.initCFindRQ(int msgID, String sopClassUID, int priority)
    rqCmd.initCFindRQ(assoc.nextMsgID(), UIDs.StudyRootQueryRetrieveInformationModelFIND, priority);

    // API doc: AssociationFactorynewDimse(int pcid, Command cmd, Dataset ds)
    // DIMSE (DICOM Message Service Element) ist ein Nachrichtendienst in DICOM
    Dimse findRq = aFact.newDimse(pc.pcid(), rqCmd, keys);

    if (DEBUG) {
      StringWriter w = new StringWriter();
      w.write("C-FIND RQ Identifier:\n");
      keys.dumpDataset(w, null);
      log.debug(w.toString());
    }

    // Invoke active association with find request Dimse
    FutureRSP future = aassoc.invoke(findRq);
    // Response to the C-FIND request.
    // The result cannot be accessed until it has been set.
    Dimse findRsp = future.get();

    // Get the list of found objects
    dimseList = future.listPending();

    // >>>> Extract Dataset from Dimse

    datasetVector = new Vector();

    // If no List of DIMSE objects was generated or it is empty return an empty Vector
    if (dimseList == null || dimseList.isEmpty()) {
      return datasetVector;
    }

    // Process all elements
    for (int i = 0; i < dimseList.size(); i++) {
      datasetVector.addElement(((Dimse) dimseList.get(i)).getDataset());
    }

    return datasetVector;
  }
  public Vector cGET(Dataset ds) throws ConnectException, IOException, InterruptedException {
    PresContext pc;
    List dimseList;
    Vector datasetVector;

    // An association must be active
    if (aassoc == null) {
      throw new ConnectException("No Association established");
    }

    // Test, if Presentation Context for C-MOVE is supported
    // API doc: Association.getAcceptedPresContext(String asuid, String tsuid)
    if ((pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelGET,
                        UIDs.ExplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelGET,
                        UIDs.ImplicitVRLittleEndian))
            == null) {
      throw new ConnectException(
          "Association does not support presentation context for StudyRootQueryRetrieveInformationModelMOVE SOP.");
    }

    // Get the Study Instance UID of the study to mode
    String suid = ds.getString(Tags.SOPInstanceUID);

    // Prepare info for logging
    String patName = ds.getString(Tags.PatientName);
    String patID = ds.getString(Tags.PatientID);
    String studyDate = ds.getString(Tags.StudyDate);
    String prompt =
        "Study[" + suid + "] from " + studyDate + " for Patient[" + patID + "]: " + patName;

    // log.info("Moving: " + prompt);

    // New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command Set Structure
    Command rqCmd = dof.newCommand();
    // API doc: Command.initCMoveRQ(int msgID, String sopClassUID, int priority, String moveDest)
    rqCmd.initCGetRSP(assoc.nextMsgID(), UIDs.StudyRootQueryRetrieveInformationModelGET, priority);
    Dataset rqDs = dof.newDataset();
    rqDs.putCS(Tags.QueryRetrieveLevel, getQueryRetrieveLevel(STUDY_LEVEL));
    // Only Unique Key allowed in C-MOVE. PS 3.4 -C.2.2.1 Attribute Types
    rqDs.putUI(Tags.SOPInstanceUID, suid);
    // API doc: AssociationFactorynewDimse(int pcid, Command cmd, Dataset ds)
    // DIMSE (DICOM Message Service Element) ist ein Nachrichtendienst in DICOM
    Dimse moveRq = aFact.newDimse(pc.pcid(), rqCmd, rqDs);

    // Invoke active association with move request Dimse
    FutureRSP future = aassoc.invoke(moveRq);
    // Response to the C-MOVE request.
    // The result cannot be accessed until it has been set.
    Dimse moveRsp = future.get();
    Command rspCmd = moveRsp.getCommand();

    if (DEBUG) {
      StringWriter w = new StringWriter();
      w.write("C-FIND RQ Identifier:\n");
      keys.dumpDataset(w, null);
      log.debug(w.toString());
    }

    // Invoke active association with find request Dimse

    // Response to the C-FIND request.
    // The result cannot be accessed until it has been set.

    // Get the list of found objects
    dimseList = future.listPending();

    // >>>> Extract Dataset from Dimse

    datasetVector = new Vector();

    // If no List of DIMSE objects was generated or it is empty return an empty Vector
    if (dimseList == null || dimseList.isEmpty()) {
      return datasetVector;
    }

    // Process all elements
    for (int i = 0; i < dimseList.size(); i++) {
      datasetVector.addElement(((Dimse) dimseList.get(i)).getDataset());
      if (((Dimse) dimseList.get(i)).getDataset() == null)
        System.out.println("              Dataset created succesffullyu          ");
    }

    // PS 3.7 - 9.3.4 C-MOVE PROTOCOL, 9.3.4.2 C-MOVE-RSP
    int status = rspCmd.getStatus();
    switch (status) {
      case 0x0000:
        // log.info("Moved: " + prompt);
        break;
      case 0xB000:
        log.error("One or more failures during move of " + prompt);
        break;
      default:
        log.error("Failed to move " + prompt + "\n\terror tstatus: " + Integer.toHexString(status));
        break;
    }
    System.out.println("The move sise is : " + datasetVector.size());
    return datasetVector;
  }
  /**
   * Stores a DICOM object in an archive (Storage SCP).
   *
   * <p>See PS 3.4 - Annex B STORAGE SERVICE CLASS.
   *
   * @param ds the Dataset to store.
   * @throws ConnectException
   * @throws ParseException
   * @throws IOException
   * @throws InterruptedException
   * @throws IllegalStateException
   */
  public void cSTORE(Dataset ds)
      throws InterruptedException, IOException, ConnectException, ParseException {
    String sopClassUID;
    String sopInstUID;
    PresContext pc = null;

    // An association must be active
    if (aassoc == null) {
      throw new ConnectException("No Association established");
    }

    // SOP Class UID must be given
    if ((sopClassUID = ds.getString(Tags.SOPClassUID)) == null) {

      throw new ParseException("No SOP Class UID in Dataset", 0);
    }

    // SOP Instance UID must be given
    if ((sopInstUID = ds.getString(Tags.SOPInstanceUID)) == null) {
      throw new ParseException("No SOP Instance UID in Dataset", 0);
    }

    // Test, if applicable presentation context was found
    if ((pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(sopClassUID, UIDs.ImplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(sopClassUID, UIDs.ExplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(sopClassUID, UIDs.ExplicitVRBigEndian))
            == null) {

      throw new ConnectException("No applicable presentation context found");
    }

    // New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command Set Structure
    Command cStoreRQ = oFact.newCommand();
    // API doc: Command.initCStoreRQ(int msgID, String sopClassUID, String sopInstUID, int priority)
    cStoreRQ.initCStoreRQ(assoc.nextMsgID(), sopClassUID, sopInstUID, priority);

    // API doc: AssociationFactorynewDimse(int pcid, Command cmd, Dataset ds)
    // DIMSE (DICOM Message Service Element) ist ein Nachrichtendienst in DICOM
    Dimse storeRq = aFact.newDimse(pc.pcid(), cStoreRQ, ds);

    // PS 3.7 - 9.3.1 C-STORE PROTOCOL, 9.3.1.2 C-STORE-RSP
    // Always returns SUCESS result code.
    // Invoke active association with echo request Dimse
    FutureRSP future = aassoc.invoke(storeRq);
    System.out.println("store is going in this AE");
    // Response to the C-ECHO request.
    // The result cannot be accessed until it has been set.
    Dimse storeRsp = future.get();
    Command rspCmd = storeRsp.getCommand();

    // PS 3.7 - 9.3.5 C-MOVE PROTOCOL, 9.3.5.2 C-ECHO-RSP
    int status = rspCmd.getStatus();
    switch (status) {
      case 0x0000:
        // Success
        break;
      default:
        log.error("C-STORE failed: " + Integer.toHexString(status));
        break;
    }
  }