LAUREL BRIDGE

LaurelBridge.DCFExamples.FrameworkConfiguration Namespace

DICOM Connectivity Framework V3.4
The FrameworkConfiguration example demonstrates different ways to configure the DCF Framework defaults.
Classes

  ClassDescription
Public classProgram
This example demonstrates several methods of loading configurations. See the class documentation for LaurelBridge.DCF.Framework for the complete list of default Framework configuration settings.
Remarks

See the class documentation for LaurelBridge.DCF.Framework for the complete list of default Framework configuration settings.

The default configuration is stored in a resource string which is combined with any overrides that may be configured by the application as demonstrated in the example code. The configuration contains an important default_session_cfg group, among others, that is used to configure the DicomSessionSettings used by many of the DCF facilities. A portion of the session configuration group (which may be out of date) is as follows:

             [ DCF.Dicom/default_session_cfg ]
             # log_level_flags may be given by a 32bit int value or by symbolic names separated by commas
             # None=0, Fatal=1, Error=2, Warn=4, Info=8, Debug=16, Verbose=32
             log_level_flags = Fatal,Error,Warn,Info,Debug
             # log_debug_flags 64bit int value or use LogDebugFlags class symbolic names separated by commas
             log_debug_flags = None
             max_read_pdu_size = 32768
             max_write_pdu_size = 32768
             ignore_max_length_negotiation = False
             favor_proposed_syntax_order = True
             decode_un_seqs_in_ile = True
             decode_undefined_length_vrs_as_sq = 
             pdu_read_timeout_seconds = 300
             pdu_write_timeout_seconds = 300
             send_dimse_timeout_seconds = 3600
             receive_dimse_timeout_seconds = 3600
             enable_streaming_mode = True
             stream_mode_buffer_size = 16384
             enable_compression_pass_through_mode = True
             enable_character_set_pass_through = False
             pdu_write_delay_seconds = 0
             pdu_read_delay_seconds = 0
             association_idle_timeout_seconds = 900
             associate_response_timeout_seconds = 30
             release_response_timeout_seconds = 5
             pre_association_script = 
             post_association_script = 
             disable_multi_pdv_pdus = True
             input_filter_cfg_name = 
             output_filter_cfg_name = 
             remove_incoming_role_selection_items = False
             remove_outgoing_role_selection_items = False
             # if set, private tags in a dataset with VR UN that are in the data dictionary
             # (via the extended data dictionary) with a different VR will be fixed to have
             # the VR from the dictionary.  If not set, a warning will be logged if the VRs are not the same
             fix_private_tags_with_vr_UN = False
             # if set, standard tags in a dataset with VR UN that are in the data dictionary
             # (via the default data dictionary) with a different VR will be fixed to have
             # the VR from the dictionary.  If not set, a warning will be logged if the VRs are not the same
             fix_standard_tags_with_vr_UN = False
             # if set, explicit transfer syntax codecs will back up and attempt to decode
             # the element header using implicit little endian when the encoded VR is not
             # one of the standard 28.  This may result in being able to read an otherwise
             # broken dataset.  A warning is printed when this occurs.
             fix_implicit_in_explicit = False
             # Defines options for element decoding for bad encodings as defined by the ElementDecodingFlags class.
             fix_bad_encoding_flags = Lenient
             # If true, the explicit transfer syntax codecs will perform a lookup for standard (non-private) tags.
             # This allows overriding the VR for explicit transfer syntaxes and should be used with caution.
             lookup_standard_vr_in_explicit = False
             # If true, the explicit transfer syntax codecs will perform a lookup for private (non-standard) tags.
             # This allows overriding the VR for explicit transfer syntaxes and should be used with caution.
             lookup_private_vr_in_explicit = False
             # This changes the actual VR of an unknown value representation to UN.  The default value is false.
             # Setting this to true may allow older implementations to better process datasets with unknown tags.
             change_unknown_explicit_vr_to_UN = False
             scu_socket_receive_buffer_size = 0
             scu_socket_send_buffer_size = 0
             # size above which DicomStreamableDataElement data will be streamed to/from
             # temp files rather than fully buffered.  If large_element_file_mode
             # is true, the DicomStreamableDataElement non pixel data from file based
             # streams will be read from the original dataset on disk when needed.
             # The value 0 disables special handling for large elements.
             large_element_threshold = 10000000
             # size in bytes above which DicomCompressedDataInput data will be stored in a temp file rather than memory
             compressed_data_input_memory_threshold = 2000000000
             # Top-level pixel data is always compressed according to the output transfer syntax.
             # Icon image sequences may be compressed in the output transfer syntax or uncompressed.
             # The following modes apply to the Icon Image Sequence (0088,0200) pixel data elements:
             # 0 = never compress
             # 1 = compress if enable_compression_pass_through_mode is true and output transfer syntax is same (pass through)
             # 2 = compress if pass through or if output transfer syntax is compressed lossless
             # 3 = always pass through or compress to output transfer syntax
             icon_image_sq_compression_mode = 0
             # The following modes apply to sequences besides the Icon Image Sequence that contain pixel data elements:
             # 0 = never compress
             # 1 = compress if enable_compression_pass_through_mode is true and output transfer syntax is same (pass through)
             # 2 = compress if pass through or if output transfer syntax is compressed lossless
             # 3 = always pass through or compress to output transfer syntax
             other_image_sq_compression_mode = 0
             # Allow DicomFileOutput to process output filters as configured in DicomSessionSettings when writing datasets to disk.
             enable_file_output_filters = False
             # Pad invalid odd length image fragments and frame offsets if present
             pad_odd_length_fragments = True
             # Allow writers to check and replace basic offset tables if offset item values are invalid.  This does a basic sanity
             # check on the offset table to check for obvious errors without verifying the pixel data.  An error message is logged
             # if the an invalid offset table is replaced.
             basic_offset_table_check = True
             # Define how writers will create the basic offset table.  Setting this option to other than that the default(0) may
             # require buffering all image data to disk when creating offset tables for non-seekable writers (eg. network writers).
             # This option is useful to make sure that multiframe images written to disk have an offset table, which in turn will
             # make image access more efficient.
             # The allowable values are as follows:
             # 0 - Create an empty offset table, which is simplest, requires no buffering, and is the default mode.
             # 1 - Create if empty or invalid, which will create a basic offset table if it does not exist.
             # 2 - Always create, which is useful to force replacement of an existing basic offset table.
             basic_offset_table_creation_mode = 0
             # Basic offset table memory threshold specifies the amount of memory that should be allowed for buffered compressed image
             # fragments while building the frame offset table before using a temp file for buffering.  If the writer stream is seekable,
             # this has no effect as the writer will seek back to write the frame offset table.  A value less than 0 will buffer all image
             # data in memory, and a value of 0 will set the overflow to tempfile to immediate.
             basic_offset_table_memory_threshold = 10000000
             # When a FrameProducer reads the basic offset table, the table is assumed to be correct if the table size matches the number
             # of frames as reported in the header.  Setting this property to true forces the FrameProducer to ignore any existing basic
             # offset table and to recompute the basic offset table from the existing pixel data. This might enable pixel data in an element
             # with an invalid table to be successfully extracted, at the expense of more processing when the dataset is initially opened.
             basic_offset_table_ignore = False
             # Fix invalid number of frames (negative or zero) for encapsulated images. Note this does not change the Number of Frame
             # in the dataset.
             fix_invalid_frame_count = True
            
             [ DCF.Dicom/default_session_cfg/supported_transfer_syntaxes ]
             # explicit-little-endian
             transfer_syntax = 1.2.840.10008.1.2.1
             # implicit-little-endian
             transfer_syntax = 1.2.840.10008.1.2
             # explicit-big-endian
             transfer_syntax = 1.2.840.10008.1.2.2
             # RLE Lossless Transfer Syntax
             transfer_syntax = 1.2.840.10008.1.2.5
             # JPEG lossless prediction selector = 1
             transfer_syntax = 1.2.840.10008.1.2.4.70
             # JPEG-LS Lossless Transfer Syntax
             transfer_syntax = 1.2.840.10008.1.2.4.80
             # JPEG2000 lossless
             transfer_syntax = 1.2.840.10008.1.2.4.90
             # JPEG-LS Lossy (Near-Lossless) Transfer Syntax
             transfer_syntax = 1.2.840.10008.1.2.4.81
             # JPEG2000 lossy
             transfer_syntax = 1.2.840.10008.1.2.4.91
             # JPEG lossless
             transfer_syntax = 1.2.840.10008.1.2.4.57
             # JPEG lossy 8 bit 
             transfer_syntax = 1.2.840.10008.1.2.4.50
             # JPEG lossy 12 bit
             transfer_syntax = 1.2.840.10008.1.2.4.51
             # MPEG2 Main Profile @ Main Level Transfer Syntax (PassThrough Mode)
             transfer_syntax = 1.2.840.10008.1.2.4.100
             # MPEG2 Main Profile @ High Level Transfer Syntax (PassThrough Mode)
             transfer_syntax = 1.2.840.10008.1.2.4.101
             # JPEG 2000 Part 2 Multi-component (Lossless Only) Transfer Syntax (PassThrough Mode)
             transfer_syntax = 1.2.840.10008.1.2.4.92
             # JPEG 2000 Part 2 Multi-component Transfer Syntax (PassThrough Mode)
             transfer_syntax = 1.2.840.10008.1.2.4.93
             

These properties define the values of the properties in a default-constructed DicomSessionSetting. Other groups in the configuration are used to configure other DCF services. See the ShowDefaultConfiguration method for an example of how to dump the entire DCF configuration.

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

FrameworkConfiguration Sample Code
public class Program
{
    private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

    /// <summary>
    /// Main entry point for the application.
    /// </summary>
    [STAThread]
    public static void Main()
    {
        try
        {
            ConfigurationUsingFramework();
            ConfigurationOverridesFromAppSettings();
            ConfigurationOverridesFromSideBySide();
            ShowDefaultConfiguration();
            DemoSessionSettingOverride();
        }
        catch (Exception e)
        {
            Logger.Error(e, "Exception caught during execution:");
            Environment.ExitCode = 1;
        }

        if (!System.Diagnostics.Debugger.IsAttached) return;
        Console.Write("Press any key to continue . . . ");
        Console.ReadKey();
    }

    /// <summary>
    /// Demonstrate dumping the DCF configuration as a string.
    /// </summary>
    private static void ShowDefaultConfiguration()
    {
        Logger.InfoFormat("Default configuration as shipped with DCF:{0}{1}",
            Environment.NewLine,
            DefaultConfigurationProvider.LoadDefaultConfiguration().DumpToString());
    }

    /// <summary>
    /// Demonstrate getting and setting a number of configuration entities via <see cref="Framework"/> methods.
    /// </summary>
    private static void ConfigurationUsingFramework()
    {
        // Get the default list of supported transfer syntaxes as a group from the configuration
        CFGGroup group = Framework.GetConfigurationGroup("DCF.Dicom/default_session_cfg/supported_transfer_syntaxes");
        Logger.InfoFormat("Default supported transfer syntaxes: {0}{1}", Environment.NewLine, group);

        // Get the default value for max concurrent associations
        int maxAssociations = Framework.GetConfigurationValue("DCF.Networking/AssociationManager/max_concurrent_associations", (int)1);
        Logger.InfoFormat("Default value: max_concurrent_associations = {0}", maxAssociations);

        // Set the value for max concurrent associations
        Framework.SetConfigurationValue("DCF.Networking/AssociationManager/max_concurrent_associations", 64);
        string attribute = Framework.GetConfigurationValue("DCF.Networking/AssociationManager/max_concurrent_associations");
        Logger.InfoFormat("After modification: max_concurrent_associations = {0}", attribute);

        // Set a configuration group
        CFGGroup customGroup = new CFGGroup("TestNameValue", new string[] { "# a comment for the group" + Environment.NewLine });
        customGroup.setAttributeBoolValue("flags", true);
        customGroup.setAttributeIntValue("int", 1);
        customGroup.setAttributeValue("string", "a string value");
        Framework.SetConfigurationGroup("test/anotherlevel", customGroup);
        CFGGroup customGroupReread = Framework.GetConfigurationGroup("test/anotherlevel/TestNameValue");
        Logger.InfoFormat("{0}Custom configuration group: {0}{1}", Environment.NewLine, customGroupReread);
    }

    private const string CustomConfigPath = "DCF.configuration.path";
    /// <summary>
    /// Demonstrate applying configuration overrides using <see cref="ConfigurationManager.AppSettings"/>.
    /// </summary>
    private static void ConfigurationOverridesFromAppSettings()
    {
        string key = "CFGProperty";

        try
        {
            // Update the app.config to point to our overrides txt file containing the override item to add.
            ConfigurationManager.AppSettings.Set(CustomConfigPath, "AppSettingsOverrides.txt");
            Logger.InfoFormat("Configuration value for key ({0}) prior to AppSettings override({1})", key, Framework.GetConfigurationValue(key));

            // Now that the app.config has been updated, reload the Framework configuration to apply our overrides.
            ConfigurationProviderFactory.Reset();
            Logger.InfoFormat("Configuration value for key({0}) added after AppSettings override({1})", key, Framework.GetConfigurationValue(key));
        }
        finally
        {
            // Cleanup our app.config
            ConfigurationManager.AppSettings.Set(CustomConfigPath, String.Empty);
        }
    }

    /// <summary>
    /// Demonstrate loading a side-by-side configuration file with a custom override.
    /// </summary>
    private static void ConfigurationOverridesFromSideBySide()
    {
        string key = "FileProperty";
        string filePath = "FrameworkConfiguration.config";

        try
        {
            WriteSideBySideConfigurationFile("FrameworkConfiguration.config");
            Logger.InfoFormat("Configuration value for key ({0}) prior to SideBySide override({1})", key, Framework.GetConfigurationValue(key));

            // Reload the Framework configuration to apply our overrides from the SideBySide configuration
            ConfigurationProviderFactory.Reset();
            Logger.InfoFormat("Configuration value for key({0}) added after SideBySide override({1})", key, Framework.GetConfigurationValue(key));
        }
        finally
        {
            // Cleanup our SideBySide configuration, and reload the Framework configuration.
            if (File.Exists(filePath))
                File.Delete(filePath);
        }
    }

    private static void WriteSideBySideConfigurationFile(string filePath)
    {
        using (StreamWriter writer = new StreamWriter(filePath))
        {
            writer.WriteLine("FileProperty = \"Value read from file\"");
        }
    }

    /// <summary>
    /// The override data could come from the appConfig, or from a cfg file, or an inline string.
    /// This particular set of overrides changes the pdu read and write timeout values, and also
    /// changes the supported transfer syntaxes group.  When changing a group, the override
    /// specification must supply all the values of the tree under the group.  The values below
    /// were picked to be different from the defaults.
    /// </summary>
    private static readonly string _overrideData = String.Join(Environment.NewLine,
        "[ my_overrides ]",
        "pdu_read_timeout_seconds = 12345",
        "pdu_write_timeout_seconds = 67890",
        "my_special_cfg_option = abcde",
        "[ my_overrides/supported_transfer_syntaxes ]",
        "# explicit-little-endian",
        "transfer_syntax = 1.2.840.10008.1.2.1",
        "# implicit-little-endian",
        "transfer_syntax = 1.2.840.10008.1.2",
        "# RLE Lossless Transfer Syntax",
        "transfer_syntax = 1.2.840.10008.1.2.5"
    );

    /// <summary>
    /// This example demonstrates how to use an external configuration to override various portions of the
    /// default session configuration.  A portion of the default session configuration may be saved in a configuration
    /// file (either as a top-level configuration group, or as a sub-group as in this example).  One of the
    /// DicomSessionSettings constructors takes this configuration group as an override parameter.
    /// </summary>
    private static void DemoSessionSettingOverride()
    {
        // load the overrides from the 'my_overrides' group
        CFGGroup overrideCfg = CFGDB.loadGroupFromString(_overrideData).getGroup("my_overrides");
        // construct the session settings with the override group
        DicomSessionSettings ss = new DicomSessionSettings(overrideCfg);
        // make sure we got the correct PduReadSeconds and PduWriteSeconds
        if (ss.PduReadTimeoutSeconds != 12345)
            throw new InvalidDataException("unexpected pdu read timeout");
        if (ss.PduWriteTimeoutSeconds != 67890)
            throw new InvalidDataException("unexpected pdu write timeout");
        // it is possible to add custom session attribute values or groups, which may be accessed via the Cfg property
        if (!ss.Cfg.getAttributeValue("my_special_cfg_option", "not there").Equals("abcde"))
            throw new InvalidDataException("unexpected my_special_cfg_option");
        // Get the supported transfer syntax list
        string[] supportedSyntaxList = ss.SupportedTransferSyntaxes.getAttribute("transfer_syntax").Values;
        if (supportedSyntaxList.Length != 3)
            throw new InvalidDataException("unexpected transfer syntax list length");
        string[] expectedSyntaxList = new string[] { Uids.ELE, Uids.ILE, Uids.RLELossless };
        if (!expectedSyntaxList.SequenceEqual(supportedSyntaxList))
            throw new InvalidDataException("unexpected transfer syntax list");
        Logger.InfoFormat("Session configuration after override:{0}{1}", Environment.NewLine, ss);
    }
}