The StatisticsExample example demonstrates how to configure a DCF scp to be able to keep session statistics.
Classes
Class | Description | |
---|---|---|
Program |
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); } }