fo-dicom / fo-dicom-samples

Sample applications associated with the fo-dicom framework
Other
153 stars 143 forks source link

Version 5.0.2 not returning C-Echo #79

Closed MarcoK80 closed 2 years ago

MarcoK80 commented 2 years ago

Hi, since version 5.0.2 (works in version 5.0.0) a normal C-Echo to a test server do not return and the server shows:

[DEBUG] [IDLE] --> [CONNECTING] [DEBUG] [CONNECTING] --> [REQUESTING ASSOCIATION] [INFO] PluginAET -> Association request: Calling AE Title: ReceiverPluginAET Called AE Title: PluginAET Remote host: localhost Remote port: 1234 Implementation Class: Implementation Class UID [1.3.6.1.4.1.30071.8] Implementation Version: fo-dicom 5.0.2 Maximum PDU Length: 0 Async Ops Invoked: 0 Async Ops Performed: 0 Presentation Contexts: 1 Presentation Context: 1 [Proposed] Abstract Syntax: Verification SOP Class Transfer Syntax: Implicit VR Little Endian: Default Transfer Syntax for DICOM [ERROR] Exception processing PDU: FellowOakDicom.Network.DicomNetworkException: Pdu[type=01, length=211] (offset=211, bytes=1, field="Item-Type") Requested offset out of range! at FellowOakDicom.Network.RawPDU.CheckOffset(Int32 bytes, String name) at FellowOakDicom.Network.RawPDU.ReadByte(String name) at FellowOakDicom.Network.AAssociateRQ.Read(RawPDU raw) at FellowOakDicom.Network.DicomService.d__71.MoveNext() [INFO] Connection closed

Code used for client:

 var dicomClient = DicomClientFactory.Create(myPluginHostName, myPluginPort, false, myReceiverAET, mypluginAET);
                    dicomClient.NegotiateAsyncOps();
                    await dicomClient.AddRequestAsync(new DicomCEchoRequest()).ConfigureAwait(false);

                    await dicomClient.SendAsync().ConfigureAwait(false);

ServerCode

 public class QRService : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider
    {
        private static string StoragePath = @".\DICOMStorage";

        private static readonly DicomTransferSyntax[] AcceptedTransferSyntaxes = new DicomTransferSyntax[]
            {
                DicomTransferSyntax.ExplicitVRLittleEndian,
                DicomTransferSyntax.ExplicitVRBigEndian,
                DicomTransferSyntax.ImplicitVRLittleEndian
            };

        private static readonly DicomTransferSyntax[] AcceptedImageTransferSyntaxes = new DicomTransferSyntax[]
            {
                // Lossless
                DicomTransferSyntax.JPEGLSLossless,
                DicomTransferSyntax.JPEG2000Lossless,
                DicomTransferSyntax.JPEGProcess14SV1,
                DicomTransferSyntax.JPEGProcess14,
                DicomTransferSyntax.RLELossless,

                // Lossy
                DicomTransferSyntax.JPEGLSNearLossless,
                DicomTransferSyntax.JPEG2000Lossy,
                DicomTransferSyntax.JPEGProcess1,
                DicomTransferSyntax.JPEGProcess2_4,

                // Uncompressed
                DicomTransferSyntax.ExplicitVRLittleEndian,
                DicomTransferSyntax.ExplicitVRBigEndian,
                DicomTransferSyntax.ImplicitVRLittleEndian
            };

        public string CallingAE { get; protected set; }
        public string CalledAE { get; protected set; }
        public IPAddress RemoteIP { get; private set; }

        public QRService(INetworkStream stream, Encoding fallbackEncoding, ILogger logger, ILogManager logManager, INetworkManager networkManager, ITranscoderManager transcoderManager) : base(stream, fallbackEncoding, logger, logManager, networkManager, transcoderManager)
        {
            var pi = stream.GetType().GetProperty("Socket", BindingFlags.NonPublic | BindingFlags.Instance);
            if (pi != null)
            {
                var endPoint = ((Socket)pi.GetValue(stream, null)).RemoteEndPoint as IPEndPoint;
                RemoteIP = endPoint.Address;
            }
            else
            {
                RemoteIP = new IPAddress(new byte[] { 127, 0, 0, 1 });
            }
            if (!Directory.Exists(StoragePath))
            {
                Directory.CreateDirectory(StoragePath);
            }
        }

        public Task<DicomCEchoResponse> OnCEchoRequestAsync(DicomCEchoRequest request)
        {
            Logger.Info($"Received verification request from AE {CallingAE} with IP: {RemoteIP}");
            return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success));
        }

        public void OnConnectionClosed(Exception exception)
        {
        }

        public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
        {
            //log the abort reason
            Logger.Info($"Received abort from {source}, reason is {reason}");
        }

        public Task OnReceiveAssociationReleaseRequestAsync()
        {
            return SendAssociationReleaseResponseAsync();
        }

        public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
        {
            CallingAE = association.CallingAE;
            CalledAE = association.CalledAE;

            Console.WriteLine($"Received association request from AE: {CallingAE} with IP: {RemoteIP} ");

            if (QRServer.AETitle != CalledAE)
            {
                Logger.Warn($"Association with {CallingAE} rejected since called aet {CalledAE} is unknown");
                return SendAssociationRejectAsync(DicomRejectResult.Permanent, DicomRejectSource.ServiceUser, DicomRejectReason.CalledAENotRecognized);
            }

            foreach (var pc in association.PresentationContexts)
            {
                if (pc.AbstractSyntax == DicomUID.Verification)
                {
                    pc.SetResult(DicomPresentationContextResult.Accept);
                }
                else if (pc.AbstractSyntax == DicomUID.PatientRootQueryRetrieveInformationModelFind
                    || pc.AbstractSyntax == DicomUID.PatientRootQueryRetrieveInformationModelMove
                    || pc.AbstractSyntax == DicomUID.StudyRootQueryRetrieveInformationModelFind
                    || pc.AbstractSyntax == DicomUID.StudyRootQueryRetrieveInformationModelMove)
                {
                    Logger.Info($"Requested abstract syntax {pc.AbstractSyntax} from {CallingAE} supported, using AcceptedTransferSyntaxes");
                    pc.AcceptTransferSyntaxes(AcceptedTransferSyntaxes);
                }
                else if (pc.AbstractSyntax == DicomUID.PatientRootQueryRetrieveInformationModelGet
                    || pc.AbstractSyntax == DicomUID.StudyRootQueryRetrieveInformationModelGet)
                {
                    Logger.Info($"Requested abstract syntax {pc.AbstractSyntax} from {CallingAE} supported, using AcceptedImageTransferSyntaxes");
                    pc.AcceptTransferSyntaxes(AcceptedImageTransferSyntaxes);
                }
                else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None)
                {
                    Logger.Info($"Requested abstract syntax {pc.AbstractSyntax} from {CallingAE} supported, using AcceptedImageTransferSyntaxes");
                    pc.AcceptTransferSyntaxes(AcceptedImageTransferSyntaxes);
                }
                else
                {
                    Logger.Warn($"Requested abstract syntax {pc.AbstractSyntax} from {CallingAE} not supported");
                    pc.SetResult(DicomPresentationContextResult.RejectAbstractSyntaxNotSupported);
                }
            }

            Logger.Info($"Accepted association request from {CallingAE}");
            return SendAssociationAcceptAsync(association);
        }

        public Task<DicomCStoreResponse> OnCStoreRequestAsync(DicomCStoreRequest request)
        {
            var studyUid = request.Dataset.GetSingleValue<string>(DicomTag.StudyInstanceUID);
            var instUid = request.SOPInstanceUID.UID;

            var path = Path.GetFullPath(StoragePath);
            path = Path.Combine(path, studyUid);

            if (!Directory.Exists(path)) Directory.CreateDirectory(path);

            path = Path.Combine(path, instUid) + ".dcm";

            request.File.Save(path);

            return Task.FromResult(new DicomCStoreResponse(request, DicomStatus.Success));
        }

        public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e)
        {
            // let library handle logging and error response
            return Task.CompletedTask;
        }
    }

    public static class QRServer
    {
        private static IDicomServer _server;

        public static string AETitle { get; set; }

        public static void Start(int port, string aet)
        {
            AETitle = aet;
            _server = DicomServerFactory.Create<QRService>(port);
        }

        public static void Stop()
        {
            _server.Dispose();
        }
    }