public async Task OnCFindRequestAsync_Pending_ShouldRespond() { var port = Ports.GetNext(); using (DicomServer.Create <PendingAsyncDicomCFindProvider>(port, logger: _logger.IncludePrefix("DicomServer"))) { var client = new Client.DicomClient("127.0.0.1", port, false, "SCU", "ANY-SCP") { Logger = _logger.IncludePrefix(typeof(DicomClient).Name) }; var responses = new ConcurrentQueue <DicomCFindResponse>(); DicomRequest.OnTimeoutEventArgs timeout = null; var request = new DicomCFindRequest(DicomQueryRetrieveLevel.Study) { OnResponseReceived = (req, res) => responses.Enqueue(res), OnTimeout = (sender, args) => timeout = args }; await client.AddRequestAsync(request).ConfigureAwait(false); await client.SendAsync().ConfigureAwait(false); Assert.Collection( responses, response1 => Assert.Equal(DicomStatus.Pending, response1.Status), response2 => Assert.Equal(DicomStatus.Pending, response2.Status), response3 => Assert.Equal(DicomStatus.Success, response3.Status) ); Assert.Null(timeout); } }
public async Task AcceptStoreContexts() { int port = Ports.GetNext(); using (DicomServer.Create <AcceptOnlyEchoStoreProvider>(port)) { var echoReq = new DicomCEchoRequest(); DicomStatus echoStatus = DicomStatus.Pending; echoReq.OnResponseReceived += (req, resp) => echoStatus = resp.Status; var storeReq = new DicomCStoreRequest(@".\Test Data\CT1_J2KI"); DicomStatus storeStatus = DicomStatus.Pending; storeReq.OnResponseReceived += (req, resp) => storeStatus = resp.Status; var filmSession = new FilmSession(DicomUID.BasicFilmSession, DicomUID.Generate()); var printReq = new DicomNCreateRequest(filmSession.SOPClassUID, filmSession.SOPInstanceUID); DicomStatus printStatus = DicomStatus.Pending; printReq.OnResponseReceived += (req, resp) => printStatus = resp.Status; var client = new Client.DicomClient("127.0.0.1", port, false, "SCU", "ANY-SCP"); await client.AddRequestsAsync(echoReq, storeReq, printReq); await client.SendAsync(); Assert.Equal(DicomStatus.Success, echoStatus); Assert.Equal(DicomStatus.Success, storeStatus); Assert.Equal(DicomStatus.SOPClassNotSupported, printStatus); } }
public async Task DicomClientSend_ToAcceptedAssociation_ShouldSendRequest() { var port = Ports.GetNext(); using (DicomServer.Create <DicomClientTest.MockCEchoProvider>(port)) { var locker = new object(); var expected = DicomStatus.Success; DicomStatus actual = null; var client = new Dicom.Network.Client.DicomClient("localhost", port, false, "SCU", "ANY-SCP"); await client.AddRequestAsync( new DicomCEchoRequest { OnResponseReceived = (rq, rsp) => { lock (locker) actual = rsp.Status; } }).ConfigureAwait(false); await client.SendAsync().ConfigureAwait(false); Assert.Equal(expected, actual); } }
public async Task DicomClientSend_ToRejectedAssociation_ShouldNotSendRequest() { var port = Ports.GetNext(); using (DicomServer.Create <DicomClientTest.MockCEchoProvider>(port)) { var locker = new object(); DicomStatus status = null; var client = new Dicom.Network.Client.DicomClient("localhost", port, false, "SCU", "WRONG-SCP"); await client.AddRequestAsync( new DicomCEchoRequest { OnResponseReceived = (rq, rsp) => { lock (locker) status = rsp.Status; } }).ConfigureAwait(false); try { await client.SendAsync().ConfigureAwait(false); } catch { } Assert.Null(status); } }
/// <summary> /// Blocks until the request is received so calling code doesn't have to deal with asynchrony (see the EventWaitHandle in TrySend). /// Only the timeout is applied no Throtelling, the client is unreleased on return /// </summary> /// <param name="client"></param> /// <param name="token"></param> #region SendRequest public void SendRequest(DicomClient client, CancellationToken token) { _listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, "Sending request to " + _dicomConfiguration.RemoteAetTitle + " at " + _dicomConfiguration.RemoteAetUri.Host + ":" + _dicomConfiguration.RemoteAetUri.Port)); bool completed; try { completed = client.SendAsync(token) .Wait(_dicomConfiguration.TransferTimeOutInMilliseconds + 1000, token); } catch (Exception ex) { OnRequestException?.Invoke(ex); throw new Exception("Error when attempting to send DICOM request: " + ex.Message, ex); } if (completed) { OnRequestSucess?.Invoke(); } else { OnRequestTimeout?.Invoke(); } }
public async Task OnCStoreRequestAsync_PreferedTransfersyntax() { var port = Ports.GetNext(); using (DicomServer.Create <AsyncDicomCStoreProviderPreferingUncompressedTS>(port, logger: _logger.IncludePrefix("DicomServer"))) { var client = new Client.DicomClient("127.0.0.1", port, false, "SCU", "ANY-SCP") { Logger = _logger.IncludePrefix(typeof(DicomClient).Name) }; int numberOfContexts = 0; DicomTransferSyntax accpetedTS = null; // create a request with a jpeg-encoded file var request = new DicomCStoreRequest(@"./Test Data/CT1_J2KI"); client.AssociationAccepted += (sender, e) => { numberOfContexts = e.Association.PresentationContexts.Count; accpetedTS = e.Association.PresentationContexts.First().AcceptedTransferSyntax; }; await client.AddRequestAsync(request).ConfigureAwait(false); await client.SendAsync().ConfigureAwait(false); Assert.Equal(2, numberOfContexts); // one for the jpeg2k TS and one for the mandatory ImplicitLittleEndian Assert.Equal(DicomTransferSyntax.JPEG2000Lossy, accpetedTS); } }
public async Task OnCFindRequestAsync_ImmediateSuccess_ShouldRespond() { var port = Ports.GetNext(); using (DicomServer.Create <ImmediateSuccessAsyncDicomCFindProvider>(port, logger: _logger.IncludePrefix("DicomServer"))) { var client = new Client.DicomClient("127.0.0.1", port, false, "SCU", "ANY-SCP") { Logger = _logger.IncludePrefix(typeof(DicomClient).Name) }; DicomCFindResponse response = null; DicomRequest.OnTimeoutEventArgs timeout = null; var request = new DicomCFindRequest(DicomQueryRetrieveLevel.Study) { OnResponseReceived = (req, res) => response = res, OnTimeout = (sender, args) => timeout = args }; await client.AddRequestAsync(request).ConfigureAwait(false); await client.SendAsync().ConfigureAwait(false); Assert.NotNull(response); Assert.Equal(DicomStatus.Success, response.Status); Assert.Null(timeout); } }
/// <summary> /// Throttle requests using W(O) = mO(t) + c where W is the wait period, O is the opertaion duration, m and c are positive constants /// Sends requests added to the client is unreleased at the end of this request send. /// </summary> /// #region ThrottleRequest public void ThrottleRequest(DicomClient client, CancellationToken cancellationToken) { var transferTimer = new Stopwatch(); transferTimer.Start(); SendRequest(client, cancellationToken); transferTimer.Stop(); //valuein mills var delay = _dicomConfiguration.RequestCooldownInMilliseconds; if (delay > 0) { _listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, "Requests sleeping for " + delay / 1000 + "seconds")); Task.Delay(delay, cancellationToken).Wait(cancellationToken); } }
public async Task DicomClientSend_StorePart10File_ShouldSucceed() { var port = Ports.GetNext(); using (var server = DicomServer.Create <CStoreScp>(port)) { server.Logger = _logger.IncludePrefix("CStoreScp"); var file = DicomFile.Open(@".\Test Data\CT-MONO2-16-ankle"); var client = new Dicom.Network.Client.DicomClient("127.0.0.1", port, false, "SCU", "SCP") { Logger = _logger.IncludePrefix("DicomClient") }; await client.AddRequestAsync(new DicomCStoreRequest(file)).ConfigureAwait(false); var exception = await Record.ExceptionAsync(async() => await client.SendAsync()); Assert.Null(exception); } }
public async Task SendMaxPDU() { var port = Ports.GetNext(); uint serverPduLength = 400000; uint clientPduLength = serverPduLength / 2; using (var server = DicomServer.Create <DicomCEchoProvider>(port, null, new DicomServiceOptions { MaxPDULength = serverPduLength })) { uint serverPduInAssociationAccepted = 0; var client = new Client.DicomClient("127.0.0.1", port, false, "SCU", "ANY-SCP"); client.Options = new DicomServiceOptions { MaxPDULength = clientPduLength }; // explicitly choose a different value await client.AddRequestAsync(new DicomCEchoRequest()); client.AssociationAccepted += (sender, e) => serverPduInAssociationAccepted = e.Association.MaximumPDULength; await client.SendAsync(); Assert.Equal(serverPduLength, serverPduInAssociationAccepted); } }
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 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)) { 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(); StudyToFetch current; int consecutiveFailures = 0; //While we have things to fetch while (studiesToOrder.TryTake(out 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) { //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); }
public Task OnReceiveAssociationAcceptAsync(DicomAssociation association) { return(DicomClient.OnReceiveAssociationAcceptAsync(association)); }
/// <summary> /// Blocks until the request is received so calling code doesn't have to deal with asynchrony (see the EventWaitHandle in TrySend). /// Only the timeout is applied no Throtelling /// </summary> /// <param name="dicomRequest"></param> #region SendRequest private void SendRequest(DicomRequest dicomRequest, CancellationToken token) { var client = new DicomClient(_dicomConfiguration.RemoteAetUri.Host, _dicomConfiguration.RemoteAetUri.Port, false, _dicomConfiguration.LocalAetTitle, _dicomConfiguration.RemoteAetTitle); SendRequest(dicomRequest, client, token); }
public Task OnReceiveAssociationRejectAsync(DicomRejectResult result, DicomRejectSource source, DicomRejectReason reason) { return(DicomClient.OnReceiveAssociationRejectAsync(result, source, reason)); }
/// <summary> /// Throttle requests using W(O) = mO(t) + c where W is the wait period, O is the opertaion duration, m and c are positive constants /// The request is added to the client which is unreleased at the end of this request send. /// </summary> /// #region ThrottleRequest public void ThrottleRequest(DicomRequest dicomRequest, DicomClient client, CancellationToken cancellationToken) { client.AddRequestAsync(dicomRequest).Wait(cancellationToken); ThrottleRequest(client, cancellationToken); }
public Task OnReceiveAssociationReleaseResponseAsync() { return(DicomClient.OnReceiveAssociationReleaseResponseAsync()); }
/// <summary> /// Blocks until the request is received so calling code doesn't have to deal with asynchrony (see the EventWaitHandle in TrySend). /// Only the timeout is applied no Throtelling, the client is unreleased on return /// </summary> /// <param name="dicomRequest"></param> /// <param name="client"></param> #region SendRequest public void SendRequest(DicomRequest dicomRequest, DicomClient client, CancellationToken token) { client.AddRequestAsync(dicomRequest).Wait(token); SendRequest(client, token); }
public Task OnConnectionClosedAsync(Exception exception) { return(DicomClient.OnConnectionClosedAsync(exception)); }
public Task OnReceiveAbortAsync(DicomAbortSource source, DicomAbortReason reason) { return(DicomClient.OnReceiveAbortAsync(source, reason)); }
public static Task <IDicomClientState> TransitionToSendingRequestsState(this DicomClient dicomClient, IInitialisationWithAssociationParameters parameters, DicomClientCancellation cancellation) { return(TransitionToSendingRequestsState(dicomClient, parameters, parameters.Association, cancellation)); }
protected override Task OnSendQueueEmptyAsync() { return(DicomClient.OnSendQueueEmptyAsync()); }
public static Task <IDicomClientState> TransitionToIdleState(this DicomClient dicomClient, DicomClientCancellation cancellation) { var idleState = new DicomClientIdleState(dicomClient); return(dicomClient.Transition(idleState, cancellation)); }
public Task OnRequestCompletedAsync(DicomRequest request, DicomResponse response) { return(DicomClient.OnRequestCompletedAsync(request, response)); }
public Task <DicomResponse> OnNEventReportRequestAsync(DicomNEventReportRequest request) { return(DicomClient.OnNEventReportRequestAsync(request)); }
public Task <DicomResponse> OnCStoreRequestAsync(DicomCStoreRequest request) { return(DicomClient.OnCStoreRequestAsync(request)); }
public Task OnRequestTimedOutAsync(DicomRequest request, TimeSpan timeout) { return(DicomClient.OnRequestTimedOutAsync(request, timeout)); }
public static Task <IDicomClientState> TransitionToConnectState(this DicomClient dicomClient, DicomClientCancellation cancellation) { var connectState = new DicomClientConnectState(dicomClient); return(dicomClient.Transition(connectState, cancellation)); }
public async Task ClientHandleNEventReport_SynchronousEvent() { var port = Ports.GetNext(); using (DicomServer.Create <SimpleStorageComitmentProvider>(port)) { DicomStatus status = null; int verifiedInstances = 0; DateTime stampNActionResponse = DateTime.MinValue; DateTime stampNEventReportRequest = DateTime.MinValue; var dicomClient = new Client.DicomClient("127.0.0.1", port, false, "SCU", "ANY-SCP"); var nActionDicomDataSet = new DicomDataset { { DicomTag.TransactionUID, DicomUIDGenerator.GenerateDerivedFromUUID().UID }, { DicomTag.ReferencedSOPSequence, new DicomDataset() { { DicomTag.ReferencedSOPClassUID, "1.2.840.10008.5.1.4.1.1.1" }, { DicomTag.ReferencedSOPInstanceUID, "1.3.46.670589.30.2273540226.4.54" } }, new DicomDataset() { { DicomTag.ReferencedSOPClassUID, "1.2.840.10008.5.1.4.1.1.1" }, { DicomTag.ReferencedSOPInstanceUID, "1.3.46.670589.30.2273540226.4.59" } } } }; var nActionRequest = new DicomNActionRequest(DicomUID.StorageCommitmentPushModel, DicomUID.StorageCommitmentPushModel, 1) { Dataset = nActionDicomDataSet, OnResponseReceived = (DicomNActionRequest request, DicomNActionResponse response) => { status = response.Status; stampNActionResponse = DateTime.Now; }, }; await dicomClient.AddRequestAsync(nActionRequest); dicomClient.OnNEventReportRequest = (eventReq) => { var refSopSequence = eventReq.Dataset.GetSequence(DicomTag.ReferencedSOPSequence); foreach (var item in refSopSequence.Items) { verifiedInstances += 1; } stampNEventReportRequest = DateTime.Now; return(Task.FromResult(new DicomNEventReportResponse(eventReq, DicomStatus.Success))); }; dicomClient.AssociationLingerTimeoutInMs = (int)TimeSpan.FromSeconds(5).TotalMilliseconds; await dicomClient.SendAsync().ConfigureAwait(false); Assert.Equal(DicomStatus.Success, status); Assert.Equal(2, verifiedInstances); Assert.True(stampNActionResponse < stampNEventReportRequest); } }
public static Task <IDicomClientState> TransitionToCompletedState(this DicomClient dicomClient, IInitialisationWithConnectionParameters parameters, DicomClientCancellation cancellation) { return(TransitionToCompletedState(dicomClient, parameters.Connection, cancellation)); }
public DicomClientConnection(DicomClient dicomClient, INetworkStream networkStream) : base(networkStream, dicomClient.FallbackEncoding, dicomClient.Logger) { DicomClient = dicomClient; NetworkStream = networkStream; }