LAUREL BRIDGE

LaurelBridge.DCF.Examples.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
            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
            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 OB, OW, OF, UN data will be streamed to/from
            # temp files rather than fully buffered.  If large_element_file_mode
            # is true, the OB, OW, OF and UN 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
            # 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 buffer 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_buffer_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
            

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.

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:");
        }

        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 whether to optimize large elements processing from DicomFileInput when on disk
        string attribute = Framework.GetConfigurationValue("DCF.Dicom/large_element_file_mode");
        Logger.InfoFormat("Default value: large_element_file_mode = {0}", attribute);

        // Set the value for an existing configuration flag
        Framework.SetConfigurationValue("DCF.Dicom/large_element_file_mode", false);
        attribute = Framework.GetConfigurationValue("DCF.Dicom/large_element_file_mode");
        Logger.InfoFormat("After modification: large_element_file_mode = {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);
    }