LAUREL BRIDGE

LaurelBridge.DCFExamples.StatisticsExample Namespace

DICOM Connectivity Framework V3.4
The StatisticsExample example demonstrates how to configure a DCF scp to be able to keep session statistics.
Classes

  ClassDescription
Public classProgram
Create and start a StatsServer, then send multiple C-Echo requests to it on multiple associations. As each association completes, information about the association will be printed to the console.
Remarks

Supported OS Platforms:

  • Windows - .Net Framework 4.7.2 64-bit and 32-bit
  • Windows - .Net Core 2.1 64-bit and 32-bit
  • Linux - .Net Core 2.1 64-bit

Examples

StatisticsExample Sample Code
public class Program
{
    /// <summary>
    /// Main entry point for StatisticsExample.
    /// </summary>
    [STAThread]
    public static void Main()
    {
        try
        {
            StatsServer server = new StatsServer(10200);
            server.BeginListening();
            Console.WriteLine("listening on {0}...", 10200);

            for (int i = 0; i < 5; i++)
            {
                using (VerificationSCU scu = new VerificationSCU("SCU", new DicomAddress("localhost", 10200, "SCP")))
                {
                    scu.RequestAssociation();
                    for (int j = 0; j <= i; j++)
                        scu.CEcho(2);
                    scu.ReleaseAssociation();
                }
            }

            server.Stop();

            if (System.Diagnostics.Debugger.IsAttached)
            {
                Console.Write("Press any key to continue . . . ");
                Console.ReadKey();
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("Exception caught during execution: {0}", e);
            Environment.ExitCode = 1;
        }
    }
}

/// <summary>
/// A simple server class that maintains an AssociationManager, registers StatsSCPs with that
/// AssociationManager, and prints out some statistics when each association ends.
/// </summary>
internal class StatsServer : AssociationListenerAdapter, IAssociationConfigPolicyManager
{
    private readonly AssociationManager _manager;
    private readonly Dictionary<string, IServiceClassProvider> _scpDictionary;

    public StatsServer(int port)
    {
        _manager = new AssociationManager();
        _manager.ServerTcpPort = port;
        _manager.AssociationConfigPolicyMgr = this;
        _manager.AddAssociationListener(this);
        _scpDictionary = new Dictionary<string, IServiceClassProvider>();
    }

    public DicomSessionSettings GetSessionSettings(AssociationAcceptor assoc)
    {
        return new DicomSessionSettings();
    }

    /// <summary>
    /// Start listening for associations via the AssociationManager.
    /// </summary>
    public void BeginListening()
    {
        Thread t = new Thread(new ThreadStart(_manager.Run));
        t.Start();
        if (!_manager.WaitForRunning(2000))
        {
            throw new TimeoutException("AssociationManager did not start in an acceptable amount of time");
        }
    }

    /// <summary>
    /// Stop receiving any new associations.
    /// </summary>
    public void Stop()
    {
        _manager.Stop();
    }

    public override void BeginAssociation(AssociationAcceptor assoc)
    {
        IServiceClassProvider scp = new StatsSCP(assoc);
        assoc.RegisterServiceClassProvider(scp);
        _scpDictionary[assoc.ConnectionID] = scp;
    }

    public override void EndAssociation(AssociationAcceptor assoc)
    {
        Console.WriteLine("*******************");
        Console.WriteLine("stats for connection {0}{1}{2}", assoc.ConnectionID, Environment.NewLine, assoc.SessionSettings.IOStatistics);
        Console.WriteLine("Counts by pdu type for connection {0}", assoc.ConnectionID);
        StatsSCP scp = _scpDictionary[assoc.ConnectionID] as StatsSCP;
        if (scp != null)
        {
            foreach (PduType type in scp.PduTypeStats.Keys)
                Console.WriteLine("{0} count: {1}", type, scp.PduTypeStats[type]);
        }

        Console.WriteLine("*******************");
        Console.WriteLine();
        _scpDictionary.Remove(assoc.ConnectionID);
    }
}

/// <summary>
/// This class maintains some simple statistics by registering as a Pdu and Dimse message listener.
/// </summary>
internal class StatsSCP : VerificationSCP, IPduFilter, IDimseMessageFilter
{
    /// <summary>
    /// A count of each pdu type this SCP has seen.
    /// </summary>
    /// <remarks>
    /// We can keep track of certain statistics in the IOStatistics object
    /// available via the DicomSessionSettings in the AssociationAcceptor, but
    /// we can also maintain a separate set of data outside of that if we choose.
    /// </remarks>
    public Dictionary<PduType, int> PduTypeStats { get; set; }

    public StatsSCP(AssociationAcceptor acceptor)
        : base(acceptor, null, null)
    {
        acceptor.RegisterDimseMessageFilter(this);
        acceptor.RegisterPduFilter(this);
        PduTypeStats = new Dictionary<PduType, int>();
    }

    public override CEchoResponse CEchoRq(CEchoRequest msg)
    {
        return new CEchoResponse(msg); // always send a successful response
    }

    public Pdu PduIn(string connectionID, DimseServiceUser dimseServiceUser, Pdu pdu)
    {
        // keep track of the total number of inbound Pdus via the object in SessionSettings
        IOStatistics stats = Acceptor.SessionSettings.IOStatistics;
        stats.AdjustPdusIn(1);
        stats.AdjustBytesIn(pdu.GetLength());

        // keep track of our Pdu-specific counts internally
        if (!PduTypeStats.ContainsKey(pdu.Type))
            PduTypeStats[pdu.Type] = 0;

        PduTypeStats[pdu.Type]++;

        return pdu;
    }

    public Pdu PduOut(string connectionID, DimseServiceUser dimseServiceUser, Pdu pdu)
    {
        // keep track of the total number of outbound Pdus via the object in SessionSettings
        IOStatistics stats = Acceptor.SessionSettings.IOStatistics;
        stats.AdjustPdusOut(1);
        stats.AdjustBytesOut(pdu.GetLength());

        // keep track of our Pdu-specific counts internally
        if (!PduTypeStats.ContainsKey(pdu.Type))
            PduTypeStats[pdu.Type] = 0;

        PduTypeStats[pdu.Type]++;

        return pdu;
    }

    public void DimseMessageIn(DimseMessage msg, DimseServiceUser dimseServiceUser)
    {
        // keep track of the total number of inbound DIMSE messages via the object in SessionSettings
        Acceptor.SessionSettings.IOStatistics.AdjustDimseMessagesIn(1);
    }

    public void DimseMessageOut(DimseMessage msg, DimseServiceUser dimseServiceUser)
    {
        // keep track of the total number of outbound DIMSE messages via the object in SessionSettings
        Acceptor.SessionSettings.IOStatistics.AdjustDimseMessagesOut(1);
    }
}