Exemple #1
0
        public override SMIDataChunk DoGetChunk(ICacheFetchRequest cacheRequest, IDataLoadEventListener listener, GracefulCancellationToken cancellationToken)
        {
            listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, $"CFindSource version is {typeof(CFindSource).Assembly.GetName().Version}.  Assembly is {typeof(PACSSource).Assembly} "));
            listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, $"Fo-Dicom version is {typeof(DicomClient).Assembly.GetName().Version}.  Assembly is {typeof(DicomClient).Assembly} "));

            var dicomConfiguration = GetConfiguration();
            var requestSender      = new DicomRequestSender(dicomConfiguration, listener);
            var dateFrom           = Request.Start;
            var dateTo             = Request.End;

            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
            };

            // Create filepath for the results of the C-Find
            var workingDirectory = cacheLayout.GetLoadCacheDirectory(listener);
            var filename         = $"{dateFrom:yyyyMMddhhmmss}.csv";
            var filepath         = Path.Combine(workingDirectory.FullName, filename);

            var sw     = new StreamWriter(filepath);
            var writer = new CsvWriter(sw, new CsvConfiguration(CultureInfo.CurrentCulture));

            WriteHeaders(writer);

            DicomClient client = new DicomClient(dicomConfiguration.RemoteAetUri.Host, dicomConfiguration.RemoteAetUri.Port, false, dicomConfiguration.LocalAetTitle, dicomConfiguration.RemoteAetTitle);

            try
            {
                // Find a list of studies
                #region Query

                listener.OnNotify(this,
                                  new NotifyEventArgs(ProgressEventType.Information,
                                                      "Requesting Studies from " + dateFrom + " to " + dateTo));
                int responses = 0;

                var request = CreateStudyRequestByDateRangeForModality(dateFrom, dateTo, Modality);
                request.OnResponseReceived += (req, response) =>
                {
                    if (Filter(Whitelist, response))
                    {
                        Interlocked.Increment(ref responses);
                        WriteResult(writer, response);
                    }
                };
                requestSender.ThrottleRequest(request, client, cancellationToken.AbortToken);
                listener.OnNotify(this,
                                  new NotifyEventArgs(ProgressEventType.Debug,
                                                      "Total filtered studies for " + dateFrom + " to " + dateTo + " is " + responses));
                #endregion
            }
            finally
            {
                writer.Dispose();
            }


            return(Chunk);
        }
Exemple #2
0
        public override SMIDataChunk DoGetChunk(ICacheFetchRequest cacheRequest, IDataLoadEventListener listener, GracefulCancellationToken cancellationToken)
        {
            listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, $"PACSSource version is {typeof(PACSSource).Assembly.GetName().Version}.  Assembly is {typeof(PACSSource).Assembly} "));
            listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, $"Fo-Dicom version is {typeof(DicomClient).Assembly.GetName().Version}.  Assembly is {typeof(DicomClient).Assembly} "));

            var dicomConfiguration = GetConfiguration();
            var requestSender      = new DicomRequestSender(dicomConfiguration, listener);
            var dateFrom           = Request.Start;
            var dateTo             = Request.End;

            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
            };

            ConcurrentBag <StudyToFetch> studiesToOrder = new ConcurrentBag <StudyToFetch>();

            CachingSCP.OnEndProcessingCStoreRequest = (storeRequest, storeResponse) =>
            {
                SaveSopInstance(storeRequest, cacheLayout, listener);
                listener.OnNotify(this,
                                  new NotifyEventArgs(ProgressEventType.Debug,
                                                      "Stored sopInstance" + storeRequest.SOPInstanceUID.UID));
            };

            //helps with tidying 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))
            {
                DicomClient client = new DicomClient(dicomConfiguration.RemoteAetUri.Host, dicomConfiguration.RemoteAetUri.Port, false, dicomConfiguration.LocalAetTitle, dicomConfiguration.RemoteAetTitle);

                try
                {
                    // Find a list of studies
                    #region Query

                    listener.OnNotify(this,
                                      new NotifyEventArgs(ProgressEventType.Information,
                                                          "Requesting Studies from " + dateFrom + " to " + dateTo));

                    var request = CreateStudyRequestByDateRangeForModality(dateFrom, dateTo, Modality);
                    request.OnResponseReceived += (req, response) =>
                    {
                        if (Filter(Whitelist, response))
                        {
                            studiesToOrder.Add(new StudyToFetch(response.Dataset.GetSingleValue <string>(DicomTag.StudyInstanceUID)));
                        }
                    };
                    requestSender.ThrottleRequest(request, client, cancellationToken.AbortToken);
                    listener.OnNotify(this,
                                      new NotifyEventArgs(ProgressEventType.Debug,
                                                          "Total filtered studies for " + dateFrom + " to " + dateTo + "is " + studiesToOrder.Count));
                    #endregion

                    //go and get them
                    #region Retrieval

                    var transferStopwatch = new Stopwatch();

                    int consecutiveFailures = 0;

                    //While we have things to fetch
                    while (studiesToOrder.TryTake(out var current))
                    {
                        transferStopwatch.Restart();
                        //delay value in mills
                        if (dicomConfiguration.TransferCooldownInMilliseconds != 0)
                        {
                            listener.OnNotify(this,
                                              new NotifyEventArgs(ProgressEventType.Information,
                                                                  "Transfers sleeping for " + dicomConfiguration.TransferCooldownInMilliseconds / 1000 + "seconds"));
                            Task.Delay(dicomConfiguration.TransferCooldownInMilliseconds, cancellationToken.AbortToken).Wait(cancellationToken.AbortToken);
                        }

                        bool done = false;

                        //Build fetch command that Study
                        var cMoveRequest = CreateCMoveByStudyUid(LocalAETitle, current.StudyUid, listener);

                        //Register callbacks
                        cMoveRequest.OnResponseReceived += (requ, response) =>
                        {
                            listener.OnNotify(this,
                                              new NotifyEventArgs(ProgressEventType.Debug,
                                                                  $"Got {response.Status.State} response for {requ}.  Items remaining {response.Remaining}"));

                            switch (response.Status.State)
                            {
                            case DicomState.Pending:
                            case DicomState.Warning:
                                // ignore
                                break;

                            case DicomState.Cancel:
                            case DicomState.Failure:
                                consecutiveFailures++;

                                if (current.RetryCount < MaxRetries)
                                {
                                    // put it back in the bag with a increased retry count
                                    current.RetryCount++;
                                    studiesToOrder.Add(current);
                                }

                                // final state
                                done = true;
                                break;

                            case DicomState.Success:
                                // final state
                                consecutiveFailures = 0;
                                done = true;
                                break;
                            }
                        };

                        //send the command to the server

                        //tell user what we are sending
                        listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, CMoveRequestToString(cMoveRequest, current.RetryCount + 1)));

                        //do not use requestSender.ThrottleRequest(cMoveRequest, cancellationToken);
                        //TODO is there any need to throtttle this request given its lifetime
                        requestSender.ThrottleRequest(cMoveRequest, client, cancellationToken.AbortToken);


                        //enforce a minimum timeout
                        var  swStudyTransfer     = Stopwatch.StartNew();
                        bool hasTransferTimedOut = false;

                        do
                        {
                            Task.Delay(Math.Max(100, dicomConfiguration.TransferPollingInMilliseconds), cancellationToken.AbortToken)
                            .Wait(cancellationToken.AbortToken);

                            hasTransferTimedOut = swStudyTransfer.ElapsedMilliseconds > dicomConfiguration.TransferTimeOutInMilliseconds;
                        }while(!done && !hasTransferTimedOut);

                        // Study has finished being fetched (or timed out)

                        if (hasTransferTimedOut)
                        {
                            listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, "Abandonning fetch of study " + current.StudyUid));
                        }

                        if (consecutiveFailures > 5)
                        {
                            throw new Exception("Too many consecutive failures, giving up");
                        }

                        // 1 failure = study not available, 2 failures = system is having a bad day?
                        if (consecutiveFailures <= 1)
                        {
                            continue;
                        }
                        //wait 4 minutes then 6 minutes then 8 minutes, eventually server will start responding again?
                        int sleepFor = consecutiveFailures * 2 * 60_000;
                        listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Warning, $"Sleeping for {sleepFor}ms due to {consecutiveFailures} consecutive failures"));

                        Task.Delay(sleepFor, cancellationToken.AbortToken)
                        .Wait(cancellationToken.AbortToken);
                    }

                    #endregion
                }
                finally
                {
                    server.Stop();
                }
            }

            return(Chunk);
        }