Troubleshooting Common Issues

Section 22 of 27
81% complete

This section covers diagnosing and resolving common DICOM problems.

Association Failures

Diagnostic Tool

public class DicomTroubleshooter
{
    public void DiagnoseAssociationFailure(string host, int port, string aeTitle)
    {
        Console.WriteLine("=== DICOM Association Diagnostics ===\n");

        // 1. Test network connectivity
        Console.WriteLine("1. Testing TCP/IP connectivity...");
        if (!TestTcpConnection(host, port))
        {
            Console.WriteLine("   FAILED: Cannot establish TCP connection");
            Console.WriteLine("   Check:");
            Console.WriteLine("      - Firewall settings");
            Console.WriteLine("      - Network connectivity (ping host)");
            Console.WriteLine("      - Correct host address and port");
            Console.WriteLine("      - VPN/network routing");
            return;
        }
        Console.WriteLine("   PASSED: TCP connection successful\n");

        // 2. Test DICOM association
        Console.WriteLine("2. Testing DICOM association...");
        DicomAssociation association = new DicomAssociation
        {
            CallingAETitle = "DIAG_SCU",
            CalledAETitle = aeTitle,
            Host = host,
            Port = port
        };

        association.AddPresentationContext(
            DicomUID.VerificationSOPClass,
            DicomTransferSyntax.ExplicitVRLittleEndian
        );

        try
        {
            association.Open();
            Console.WriteLine("   PASSED: Association established\n");

            // 3. Check presentation contexts
            Console.WriteLine("3. Checking presentation contexts...");
            CheckPresentationContexts(association);

            // 4. Test C-ECHO
            Console.WriteLine("\n4. Testing C-ECHO...");
            TestEcho(association);

            association.Close();
        }
        catch (DicomNetworkException ex)
        {
            Console.WriteLine($"   FAILED: {ex.Message}\n");
            ProvideFailureGuidance(ex.Message, aeTitle);
        }
    }

    private bool TestTcpConnection(string host, int port)
    {
        try
        {
            using (var client = new System.Net.Sockets.TcpClient())
            {
                var result = client.BeginConnect(host, port, null, null);
                bool success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5));
                if (success) client.EndConnect(result);
                return success;
            }
        }
        catch
        {
            return false;
        }
    }

    private void CheckPresentationContexts(DicomAssociation association)
    {
        foreach (PresentationContext pc in association.PresentationContexts)
        {
            string status = pc.Result == PresentationContextResult.Accept
                ? "Accepted" : "Rejected";
            Console.WriteLine($"   {status}:");
            Console.WriteLine($"      SOP Class: {GetSOPClassName(pc.AbstractSyntax)}");
            Console.WriteLine($"      Transfer Syntax: {GetTransferSyntaxName(pc.SelectedTransferSyntax)}");

            if (pc.Result != PresentationContextResult.Accept)
            {
                Console.WriteLine($"      Reason: {pc.Result}");
            }
        }
    }

    private void TestEcho(DicomAssociation association)
    {
        try
        {
            DicomCommandSet request = new DicomCommandSet(DicomCommandType.C_ECHO_RQ);
            request.AffectedSOPClassUID = DicomUID.VerificationSOPClass;
            request.MessageID = 1;

            DicomCommandSet response = association.SendRequest(request, null);

            if (response.Status == 0)
            {
                Console.WriteLine("   PASSED: C-ECHO successful");
            }
            else
            {
                Console.WriteLine($"   FAILED: C-ECHO returned status 0x{response.Status:X4}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"   FAILED: {ex.Message}");
        }
    }

    private void ProvideFailureGuidance(string errorMessage, string aeTitle)
    {
        if (errorMessage.Contains("Called AE") || errorMessage.Contains("AE Title"))
        {
            Console.WriteLine("   Check:");
            Console.WriteLine($"      - Called AE Title '{aeTitle}' is correct");
            Console.WriteLine("      - AE Title is configured on remote system");
            Console.WriteLine("      - AE Title is case-sensitive");
            Console.WriteLine("      - No extra spaces in AE Title");
        }
        else if (errorMessage.Contains("timeout"))
        {
            Console.WriteLine("   Check:");
            Console.WriteLine("      - Remote server is running");
            Console.WriteLine("      - Network latency issues");
            Console.WriteLine("      - Server is not overloaded");
            Console.WriteLine("      - Increase timeout values");
        }
        else if (errorMessage.Contains("rejected"))
        {
            Console.WriteLine("   Check:");
            Console.WriteLine("      - Calling AE Title is authorized");
            Console.WriteLine("      - IP address is in whitelist");
            Console.WriteLine("      - Presentation context is supported");
        }
    }

    private string GetSOPClassName(string uid)
    {
        return uid switch
        {
            "1.2.840.10008.5.1.4.1.1.2" => "CT Image Storage",
            "1.2.840.10008.5.1.4.1.1.4" => "MR Image Storage",
            "1.2.840.10008.5.1.4.1.1.7" => "Secondary Capture",
            "1.2.840.10008.1.1" => "Verification SOP Class",
            _ => uid
        };
    }

    private string GetTransferSyntaxName(string uid)
    {
        return uid switch
        {
            "1.2.840.10008.1.2" => "Implicit VR Little Endian",
            "1.2.840.10008.1.2.1" => "Explicit VR Little Endian",
            "1.2.840.10008.1.2.4.70" => "JPEG Lossless",
            "1.2.840.10008.1.2.4.90" => "JPEG 2000 Lossless",
            _ => uid
        };
    }
}

Storage Failures

Storage Diagnostic Tool

public void DiagnoseStorageFailure(string filePath)
{
    Console.WriteLine("=== DICOM Storage Diagnostics ===\n");

    if (!File.Exists(filePath))
    {
        Console.WriteLine($"File not found: {filePath}");
        return;
    }

    try
    {
        DicomDataSet dataset = new DicomDataSet();
        dataset.Read(filePath);

        Console.WriteLine("File read successfully\n");

        // Check required tags
        Console.WriteLine("1. Checking required tags...");
        CheckRequiredTags(dataset);

        // Check UID validity
        Console.WriteLine("\n2. Checking UID validity...");
        CheckUIDValidity(dataset);

        // Check pixel data
        if (dataset.Contains(DicomTag.PixelData))
        {
            Console.WriteLine("\n3. Checking pixel data...");
            CheckPixelData(dataset);
        }

        // Check transfer syntax
        Console.WriteLine("\n4. Checking transfer syntax...");
        CheckTransferSyntax(dataset);
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error reading file: {ex.Message}");
    }
}

private void CheckRequiredTags(DicomDataSet dataset)
{
    var requiredTags = new Dictionary<string, string>
    {
        { DicomTag.SOPClassUID, "SOP Class UID" },
        { DicomTag.SOPInstanceUID, "SOP Instance UID" },
        { DicomTag.StudyInstanceUID, "Study Instance UID" },
        { DicomTag.SeriesInstanceUID, "Series Instance UID" },
        { DicomTag.PatientName, "Patient Name" },
        { DicomTag.PatientID, "Patient ID" },
        { DicomTag.Modality, "Modality" }
    };

    bool allPresent = true;
    foreach (var tag in requiredTags)
    {
        if (!dataset.Contains(tag.Key) ||
            string.IsNullOrEmpty(dataset[tag.Key]?.Value?.ToString()))
        {
            Console.WriteLine($"   MISSING: {tag.Value} ({tag.Key})");
            allPresent = false;
        }
        else
        {
            Console.WriteLine($"   Present: {tag.Value}");
        }
    }

    if (!allPresent)
    {
        Console.WriteLine("\n   Fix: Ensure all Type 1 and Type 2 tags are present");
    }
}

private void CheckUIDValidity(DicomDataSet dataset)
{
    var uidTags = new[]
    {
        DicomTag.SOPClassUID,
        DicomTag.SOPInstanceUID,
        DicomTag.StudyInstanceUID,
        DicomTag.SeriesInstanceUID
    };

    foreach (var tag in uidTags)
    {
        if (dataset.Contains(tag))
        {
            string uid = dataset[tag]?.Value as string;
            if (IsValidUID(uid))
            {
                Console.WriteLine($"   Valid: {tag}");
            }
            else
            {
                Console.WriteLine($"   Invalid: {tag} = '{uid}'");
                Console.WriteLine($"      UIDs must contain only digits and dots");
            }
        }
    }
}

private bool IsValidUID(string uid)
{
    if (string.IsNullOrEmpty(uid)) return false;
    return System.Text.RegularExpressions.Regex.IsMatch(uid, @"^[\d.]+$") &&
           uid.Length <= 64;
}

private void CheckPixelData(DicomDataSet dataset)
{
    try
    {
        int rows = int.Parse(dataset[DicomTag.Rows]?.Value as string ?? "0");
        int cols = int.Parse(dataset[DicomTag.Columns]?.Value as string ?? "0");
        int bitsAllocated = int.Parse(dataset[DicomTag.BitsAllocated]?.Value as string ?? "0");
        int samplesPerPixel = int.Parse(dataset[DicomTag.SamplesPerPixel]?.Value as string ?? "1");

        long expectedSize = (long)rows * cols * (bitsAllocated / 8) * samplesPerPixel;

        object pixelData = dataset[DicomTag.PixelData]?.Value;
        long actualSize = 0;

        if (pixelData is byte[] byteArray)
            actualSize = byteArray.Length;
        else if (pixelData is short[] shortArray)
            actualSize = shortArray.Length * 2;

        Console.WriteLine($"   Image: {cols}x{rows}");
        Console.WriteLine($"   Bits Allocated: {bitsAllocated}");
        Console.WriteLine($"   Expected size: {expectedSize} bytes");
        Console.WriteLine($"   Actual size: {actualSize} bytes");

        if (actualSize == expectedSize)
            Console.WriteLine($"   Pixel data size is correct");
        else if (actualSize == 0)
            Console.WriteLine($"   Pixel data is missing");
        else
            Console.WriteLine($"   Size mismatch - may be compressed");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"   Error: {ex.Message}");
    }
}

private void CheckTransferSyntax(DicomDataSet dataset)
{
    string transferSyntax = dataset.TransferSyntaxUID;
    string tsName = GetTransferSyntaxName(transferSyntax);

    Console.WriteLine($"   Transfer Syntax: {tsName}");
    Console.WriteLine($"   UID: {transferSyntax}");

    if (transferSyntax == "1.2.840.10008.1.2" ||
        transferSyntax == "1.2.840.10008.1.2.1" ||
        transferSyntax == "1.2.840.10008.1.2.2")
    {
        Console.WriteLine($"   Uncompressed format (widely supported)");
    }
    else
    {
        Console.WriteLine($"   Compressed format (verify receiver supports this)");
    }
}

Transfer Syntax Conversion

public class TransferSyntaxHelper
{
    public void ConvertTransferSyntax(
        string inputFile, string outputFile, string targetTransferSyntax)
    {
        Console.WriteLine($"Converting transfer syntax...");

        DicomDataSet ds = new DicomDataSet();
        ds.Read(inputFile);

        string originalTS = ds.TransferSyntaxUID;
        Console.WriteLine($"Original: {GetTransferSyntaxName(originalTS)}");
        Console.WriteLine($"Target: {GetTransferSyntaxName(targetTransferSyntax)}");

        try
        {
            ds.Write(outputFile, targetTransferSyntax);
            Console.WriteLine("Conversion successful");

            long originalSize = new FileInfo(inputFile).Length;
            long newSize = new FileInfo(outputFile).Length;
            Console.WriteLine($"Original: {originalSize / 1024} KB");
            Console.WriteLine($"New: {newSize / 1024} KB");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Conversion failed: {ex.Message}");
            Console.WriteLine("Attempting decompression approach...");

            DecompressAndRecompress(ds, outputFile, targetTransferSyntax);
        }
    }

    private void DecompressAndRecompress(
        DicomDataSet ds, string outputFile, string targetTransferSyntax)
    {
        try
        {
            // First decompress to uncompressed format
            string tempFile = Path.GetTempFileName();
            ds.Write(tempFile, DicomTransferSyntax.ExplicitVRLittleEndian);

            // Read uncompressed version
            DicomDataSet uncompressed = new DicomDataSet();
            uncompressed.Read(tempFile);

            // Write with target transfer syntax
            uncompressed.Write(outputFile, targetTransferSyntax);

            File.Delete(tempFile);
            Console.WriteLine("Conversion successful via decompression");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Alternative approach failed: {ex.Message}");
        }
    }

    private string GetTransferSyntaxName(string uid)
    {
        return uid switch
        {
            "1.2.840.10008.1.2" => "Implicit VR Little Endian",
            "1.2.840.10008.1.2.1" => "Explicit VR Little Endian",
            "1.2.840.10008.1.2.4.70" => "JPEG Lossless",
            "1.2.840.10008.1.2.4.90" => "JPEG 2000 Lossless",
            "1.2.840.10008.1.2.4.91" => "JPEG 2000 Lossy",
            "1.2.840.10008.1.2.5" => "RLE Lossless",
            _ => uid
        };
    }
}

Common Error Codes

StatusNameDescriptionAction
0x0000SuccessOperation completedNone needed
0x0122SOP Class Not SupportedSCP doesn’t support this SOP ClassCheck SCP capabilities
0xA700Out of ResourcesSCP cannot store (disk full, etc.)Check server resources
0xA900Data Set MismatchData doesn’t match SOP ClassValidate DICOM file
0xC000Cannot UnderstandUnable to process requestCheck request format
0xFF00PendingMore results comingContinue receiving

Systematic troubleshooting helps quickly identify and resolve DICOM issues.

Quiz: Troubleshooting Common Issues

Question 1 of 4

What should you check first when a DICOM association fails?