public override SMIDataChunk DoGetChunk(ICacheFetchRequest cacheRequest, IDataLoadEventListener listener, GracefulCancellationToken cancellationToken) { #region assigns var dicomConfiguration = GetConfiguration(); var requestSender = new DicomRequestSender(dicomConfiguration, listener); var dateFrom = Request.Start; var dateTo = Request.End; var hasTransferTimedOut = false; CachingSCP.LocalAet = LocalAETitle; CachingSCP.Listener = listener; if (PatientIdWhitelistColumnInfo != null && !IgnoreWhiteList) { GetWhitelist(listener); } //temp dir var cacheDir = new LoadDirectory(Request.CacheProgress.LoadProgress.LoadMetadata.LocationOfFlatFiles).Cache; var cacheLayout = new SMICacheLayout(cacheDir, new SMICachePathResolver(Modality)); Chunk = new SMIDataChunk(Request) { FetchDate = dateFrom, Modality = Modality, Layout = cacheLayout }; // IOrder order = new ItemsBasedOrder(dateFrom, dateTo, PlacementMode.PlaceThenFill,OrderLevel, listener); IOrder order = new HierarchyBasedOrder(dateFrom, dateTo, PlacementMode.PlaceThenFill, OrderLevel, listener); IPicker picker = null; var pickerFilled = false; var transferTimeOutTimer = new Timer(dicomConfiguration.TransferTimeOutInMilliseconds); transferTimeOutTimer.Elapsed += (source, eventArgs) => { hasTransferTimedOut = true; listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, "Transfer Timeout Exception Generated")); throw new TimeoutException("Transfer Timeout Exception"); }; CachingSCP.OnEndProcessingCStoreRequest = (storeRequest, storeResponse) => { var item = new Item(storeRequest); transferTimeOutTimer.Reset(); if (picker != null) { picker.Fill(item); pickerFilled = picker.IsFilled(); } SaveSopInstance(storeRequest, cacheLayout, listener); listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Debug, "Stored sopInstance" + storeRequest.SOPInstanceUID.UID)); }; #endregion //helps with tyding up resources if we abort or through an exception and neatly avoids -> Access to disposed closure using (var server = (DicomServer <CachingSCP>)DicomServer.Create <CachingSCP>(dicomConfiguration.LocalAetUri.Port)) { try { // Find a list of studies #region Query listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, "Requesting Studies from " + dateFrom + " to " + dateTo)); var studyUids = new List <string>(); var request = CreateStudyRequestByDateRangeForModality(dateFrom, dateTo, Modality); request.OnResponseReceived += (req, response) => { if (Filter(_whitelist, response)) { studyUids.Add(response.Dataset.GetSingleValue <string>(DicomTag.StudyInstanceUID)); } }; requestSender.ThrottleRequest(request, cancellationToken); listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Debug, "Total filtered studies for " + dateFrom + " to " + dateTo + "is " + studyUids.Count)); foreach (var studyUid in studyUids) { listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Debug, "Sending series query for study" + studyUid)); var seriesUids = new List <string>(); request = CreateSeriesRequestByStudyUid(studyUid); request.OnResponseReceived += (req, response) => { if (response.Dataset == null) { return; } var seriesInstanceUID = response.Dataset.GetSingleValue <string>(DicomTag.SeriesInstanceUID); if (seriesInstanceUID != null) { seriesUids.Add(seriesInstanceUID); } }; requestSender.ThrottleRequest(request, cancellationToken); listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Debug, "Total series for " + studyUid + "is " + seriesUids.Count)); foreach (var seriesUid in seriesUids) { listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Debug, "Sending image query for series" + seriesUid)); request = CreateSopRequestBySeriesUid(seriesUid); int imageCount = 0; request.OnResponseReceived += (req, response) => { if (response.Dataset == null) { return; } var sopUid = response.Dataset.GetSingleValue <string>(DicomTag.SOPInstanceUID); var patientId = response.Dataset.GetSingleValue <string>(DicomTag.PatientID); if (sopUid != null && patientId != null) { //Place order order.Place(patientId, studyUid, seriesUid, sopUid); imageCount++; } }; requestSender.ThrottleRequest(request, cancellationToken); listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Debug, "Successfully finished image query for " + seriesUid + " Toal images in series = " + imageCount)); } listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Debug, "Successfully finished series query for " + studyUid)); } listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Debug, "Successfully finished query phase")); #endregion //go and get them #region Retrieval var transferStopwatch = new Stopwatch(); //start building request to fill orders //get the picker - the for loop avoids sleeping after all the transfers have finished and attempting dequeue on empty queue for (int delay = 0, transferTimerPollingPeriods; order.HasNextPicker() && !hasTransferTimedOut; delay = (int)(dicomConfiguration.TransferDelayFactor * transferTimerPollingPeriods * dicomConfiguration.TransferPollingInMilliseconds) + dicomConfiguration.TransferCooldownInMilliseconds ) { transferStopwatch.Restart(); //delay value in mills if (delay != 0) { listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, "Transfers sleeping for " + delay / 1000 + "seconds")); Task.Delay(delay, cancellationToken.AbortToken).Wait(cancellationToken.AbortToken); } //set this here prior to request pickerFilled = false; transferTimerPollingPeriods = 0; //get next picker picker = order.NextPicker(); // A CMove will be performed if the storescp exists and this storescp is known to the QRSCP: var cMoveRequest = picker.GetDicomCMoveRequest(LocalAETitle); /* this won't work which means we cannot enforce (low) priority * cMoveRequest.Priority=DicomPriority.Low;*/ cMoveRequest.OnResponseReceived += (requ, response) => { if (response.Status.State == DicomState.Pending) { listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Debug, "Request: " + requ.ToString() + "items remaining: " + response.Remaining)); } else if (response.Status.State == DicomState.Success) { listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Debug, "Request: " + requ.ToString() + "completed successfully")); } else if (response.Status.State == DicomState.Failure) { listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Debug, "Request: " + requ.ToString() + "failed to download: " + response.Failures)); } }; listener.OnProgress(this, new ProgressEventArgs(CMoveRequestToString(cMoveRequest), new ProgressMeasurement(picker.Filled(), ProgressType.Records, picker.Total()), transferStopwatch.Elapsed)); //do not use requestSender.ThrottleRequest(cMoveRequest, cancellationToken); //TODO is there any need to throtttle this request given its lifetime DicomClient client = new DicomClient(); requestSender.ThrottleRequest(cMoveRequest, client, cancellationToken); transferTimeOutTimer.Reset(); while (!pickerFilled && !hasTransferTimedOut) { Task.Delay(dicomConfiguration.TransferPollingInMilliseconds, cancellationToken.AbortToken) .Wait(cancellationToken.AbortToken); transferTimerPollingPeriods++; } transferTimeOutTimer.Stop(); client.Release(); listener.OnProgress(this, new ProgressEventArgs(CMoveRequestToString(cMoveRequest), new ProgressMeasurement(picker.Filled(), ProgressType.Records, picker.Total()), transferStopwatch.Elapsed)); } #endregion } finally { server.Stop(); } } return(Chunk); }