@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; }
@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; }
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()); } } }
@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; } }