This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram MajorVersion="1" MinorVersion="1">
<Class Name="DTS.Common.Core.Config.DTSConfig" Collapsed="true">
<Position X="0.5" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AIAQAAAEAACAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAIA=</HashCode>
<FileName>Config\DTSConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="DTS.Common.Core.DTSConstants" Collapsed="true">
<Position X="2.25" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAgAAAAA=</HashCode>
<FileName>DTSConstants.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="DTS.Common.Core.EventManager.EventManager" Collapsed="true">
<Position X="4" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAIIAAACAAGAEAACAAAAAACAAAAQAAAAABAAAA=</HashCode>
<FileName>EventManager\EventManager.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="DTS.Common.Core.EventManager.EventMetaData&lt;T&gt;" Collapsed="true">
<Position X="5.75" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA=</HashCode>
<FileName>EventManager\EventManager.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="DTS.Common.Core.PluginLib.PluginConfig" Collapsed="true">
<Position X="4" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAgAAAIAAAAAAAAAAAAAA=</HashCode>
<FileName>PluginLib\PluginConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="DTS.Common.Core.PluginLib.PluginConfigData" Collapsed="true">
<Position X="5.75" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAA=</HashCode>
<FileName>PluginLib\PluginConfigData.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="DTS.Common.Core.PluginLib.PluginConfigSectionHandler" Collapsed="true">
<Position X="0.5" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>PluginLib\PluginConfigSectionHandler.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="DTS.Common.Core.PluginLib.FilterHashKeyCollection" Collapsed="true">
<Position X="2.25" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAAAAAAACAAAAAAAAAABAAAAAAAAAAAAAAA=</HashCode>
<FileName>PluginLib\PluginConfigSectionHandler.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="DTS.Common.Core.PluginLib.FilterHashElement" Collapsed="true">
<Position X="0.5" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAA=</HashCode>
<FileName>PluginLib\PluginConfigSectionHandler.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="DTS.Common.Core.PluginLib.PluginManager" Collapsed="true">
<Position X="2.25" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AEAABAAAAAAEAACAAAMAAAAAAAAAAAAAAgAAAAAAAAA=</HashCode>
<FileName>PluginLib\PluginManager.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="DTS.Common.Core.ServiceManager.ServiceManager" Collapsed="true">
<Position X="4" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>gAAAAAAAAAAAAABAAEAAAAABAAAAAAAAAAAAAAFAAAA=</HashCode>
<FileName>ServiceManager\ServiceManager.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="DTS.Common.Core.ServiceManager.ServicePublishedEvent" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="5.75" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAA=</HashCode>
<FileName>ServiceManager\ServicePublishedEvent.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="DTS.Common.Core.Settings.SettingsChangedEventArgs&lt;TKey, TItem&gt;" Collapsed="true">
<Position X="0.5" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAQAAAEAAAAEAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Settings\SettingsChangedEventArgs.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="DTS.Common.Core.Settings.SettingsCollection&lt;TKey, TItem&gt;" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="2.25" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>CAIAAAAAAAAEABAQCAAABAQAABAAAAAEAAAgAABwKAA=</HashCode>
<FileName>Settings\SettingsCollection.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Interface Name="DTS.Common.Core.ServiceManager.IServicePublishedEvent" Collapsed="true">
<Position X="0.5" Y="4.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAA=</HashCode>
<FileName>ServiceManager\IServicePublishedEvent.cs</FileName>
</TypeIdentifier>
</Interface>
<Enum Name="DTS.Common.Core.EventManager.EventDiagnosticType" Collapsed="true">
<Position X="2.25" Y="5.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAABAAAAAAAAAAAAAEIACAAAAAAABAAAA=</HashCode>
<FileName>EventManager\EventManager.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="DTS.Common.Core.Settings.ChangeSettingType" Collapsed="true">
<Position X="0.5" Y="5.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAIAAAAAAAAAAABAABAABAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Settings\SettingsChangedEventArgs.cs</FileName>
</TypeIdentifier>
</Enum>
<Delegate Name="DTS.Common.Core.EventManager.SubscriberCallbackDelegate&lt;T&gt;" Collapsed="true">
<Position X="2.25" Y="6.75" Width="1.5" />
<TypeIdentifier>
<HashCode>BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>EventManager\EventManager.cs</FileName>
</TypeIdentifier>
</Delegate>
<Delegate Name="DTS.Common.Core.EventManager.DiagnosticCallbackDelegate" Collapsed="true">
<Position X="0.5" Y="6.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAACAAEAAAAAAAAAEAAAABAAAAAAAAAAAAAA=</HashCode>
<FileName>EventManager\EventManager.cs</FileName>
</TypeIdentifier>
</Delegate>
<Font Name="Segoe UI" Size="9" />
</ClassDiagram>

View File

@@ -0,0 +1,46 @@
using System.Configuration;
namespace DTS.Common.Core.PluginLib
{
/// <summary>
/// Support class that parses data from DTS.Common.Core.PluginLib.Config configuration section
/// </summary>
public class PluginConfigSectionHandler : ConfigurationSection
{
[ConfigurationProperty("PluginFolders")]
public FilterHashKeyCollection HashKeys => (FilterHashKeyCollection)base["PluginFolders"];
}
[ConfigurationCollection(typeof(FilterHashElement))]
public class FilterHashKeyCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new FilterHashElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((FilterHashElement)element).Key;
}
public FilterHashElement this[int idx] => (FilterHashElement)BaseGet(idx);
}
public class FilterHashElement : ConfigurationElement
{
[ConfigurationProperty("key", DefaultValue = "", IsKey = true, IsRequired = true)]
public string Key
{
get => (string)base["key"];
set => base["key"] = value;
}
[ConfigurationProperty("value", DefaultValue = "", IsKey = false, IsRequired = false)]
public string Value
{
get => (string)base["value"];
set => base["value"] = value;
}
}
}

View File

@@ -0,0 +1,100 @@
using DTS.Common.Utilities.Logging;
using System;
using System.Configuration;
using System.Linq;
// ReSharper disable InconsistentNaming
// ReSharper disable EmptyConstructor
namespace DTS.Common.Core.Config
{
public static class DTSConfig
{
private static string AltConfigPath;
public static string AltConfigPathGet()
{
lock (MyLock)
{
return AltConfigPath;
}
}
public static void AltConfigPathSet(string path)
{
lock (MyLock)
{
AltConfigPath = path;
}
}
// static constructor
static DTSConfig()
{
}
public static void DTSConfigInit(string path)
{
SetAltConfigPath(path);
}
private static object MyLock = new object();
/// <summary>
/// Static variable to hold alternate configuration file
/// </summary>
private static Configuration AltConfig;
public static Configuration GetAltConfig()
{
lock (MyLock)
{
return AltConfig;
}
}
/// <summary>
/// Static variable to hold alternate configuration file
/// </summary>
/// <history>
///
/// </history>
public static void SetAltConfigPath(string path)
{
lock (MyLock)
{
AltConfigPath = path;
var dtsConfig = new ExeConfigurationFileMap
{
ExeConfigFilename = string.IsNullOrEmpty(AltConfigPath) ? DTSConstants.CustomConfigPath : AltConfigPath
};
AltConfig = ConfigurationManager.OpenMappedExeConfiguration(dtsConfig, ConfigurationUserLevel.None, true);
}
}
/// <summary>
/// Static method to retrieve a setting from config file
/// </summary>
/// <param name="key">string</param>
/// <returns>string</returns>
public static string GetAppSetting(string key)
{
var appSetting = AltConfig.AppSettings.Settings.Cast<KeyValueConfigurationElement>().FirstOrDefault(setting => setting.Key == key);
if (appSetting == null)
{
APILogger.Log(
$"## AltConfig.AppSettings.Settings.Cast<KeyValueConfigurationElement>().FirstOrDefault(setting => setting.Key == key) is null for key={key}");
}
return appSetting == null ? string.Empty : appSetting.Value;
}
/// <summary>
/// Static method to get a section from config file. Used by plugin code to get plugin library section.
/// </summary>
/// <param name="sectionName">string</param>
/// <returns>object</returns>
public static object GetSection(string sectionName)
{
var section = AltConfig.GetSection(sectionName);
if (section == null)
{
APILogger.Log($"## AltConfig.GetSection(sectionName) is null for sectionName={sectionName}");
}
return section;
}
}
}

View File

@@ -0,0 +1,84 @@
using System;
namespace DTS.Common.Core.Settings
{
/// <summary>
/// Event arguments describing change to settings collection
/// </summary>
/// <typeparam name="TKey">key type used in collection</typeparam>
/// <typeparam name="TItem">value type used in collection</typeparam>
public class SettingsChangedEventArgs<TKey, TItem> : EventArgs
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="changeType">type of scenario change</param>
public SettingsChangedEventArgs(ChangeSettingType changeType)
{
ChangeType = changeType;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="changeType">change type</param>
/// <param name="key">key type used in collection</param>
public SettingsChangedEventArgs(ChangeSettingType changeType, TKey key)
{
ChangeType = changeType;
Key = key;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="changeType">change type</param>
/// <param name="key">key type used in collection</param>
/// <param name="item">value type used in collection</param>
public SettingsChangedEventArgs(ChangeSettingType changeType, TKey key, TItem item)
{
ChangeType = changeType;
Key = key;
Item = item;
}
/// <summary>
/// Returns type of scenario change
/// </summary>
public ChangeSettingType ChangeType
{
get;
private set;
}
/// <summary>
/// key associated with changed setting
/// </summary>
public TKey Key
{
get;
private set;
}
/// <summary>
/// Value associated with changed settting
/// </summary>
public TItem Item
{
get;
private set;
}
}
/// <summary>
/// Type of settings changed
/// </summary>
public enum ChangeSettingType
{
Add = 0,
Remove = 1,
Modified = 3,
ClearAll = 4
}
}

View File

@@ -0,0 +1,9 @@
// ReSharper disable InconsistentNaming
namespace DTS.Common.Core
{
public static class DTSConstants
{
public const string CustomConfigPath = @"C:\dev\DTS.Viewer\bin\Debug\DTS.Viewer.exe.config";
public const string ViewerConfigPath = @"C:\dev\DTS.Viewer\bin\Debug\DTS.Viewer.exe.config";
}
}

View File

@@ -0,0 +1,193 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace DTS.Common.Core.EventManager
{
/// <summary>
/// Delegate used by event listeners
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item"></param>
public delegate void SubscriberCallbackDelegate<in T>(T item) where T : class;
public delegate void DiagnosticCallbackDelegate(EventDiagnosticType eventType, Type t, object eventData, string listener);
/// <summary>
/// Allows components to publish events without knowing who is listening
/// </summary>
public static class EventManager
{
/// <summary>
/// Tracks listeners
/// </summary>
private static readonly Dictionary<Type, List<object>> SubscriberList = new Dictionary<Type, List<object>>();
/// <summary>
/// Tracks diagnostic listeners
/// </summary>
private static readonly List<DiagnosticCallbackDelegate> DiagnosticList = new List<DiagnosticCallbackDelegate>();
/// <summary>
/// Publish an event
/// </summary>
/// <typeparam name="T">type of event</typeparam>
/// <param name="eventData">event listener</param>
public static void Publish<T>(T eventData) where T : class
{
if (!SubscriberList.ContainsKey(typeof(T))) return;
var listeners = SubscriberList[typeof(T)];
foreach (var listener in listeners)
{
var metaData = listener as EventMetaData<T>;
if (metaData == null) continue;
var triggerCallback = true;
if (metaData.EventFilter != null)
{
triggerCallback = metaData.EventFilter(eventData);
}
if (triggerCallback)
{
metaData.Callback(eventData);
}
SendDiagnosticEvent(EventDiagnosticType.PublishEvent, typeof(T), eventData, metaData.EventFilter?.Method);
}
}
/// <summary>
/// Subscribe to an event
/// </summary>
/// <typeparam name="T">type of event</typeparam>
/// <param name="listener">event listener</param>
public static void Subscribe<T>(SubscriberCallbackDelegate<T> listener) where T : class
{
Subscribe(listener, null);
}
/// <summary>
/// Subscribe to an event
/// </summary>
/// <typeparam name="T">type of event</typeparam>
/// <param name="listener">event listener</param>
/// <param name="eventFilter">predicate to filter events sent to event listener</param>
public static void Subscribe<T>(SubscriberCallbackDelegate<T> listener, Predicate<T> eventFilter) where T : class
{
if (!SubscriberList.ContainsKey(typeof(T)))
{
SubscriberList.Add(typeof(T), new List<object>());
}
var listeners = SubscriberList[typeof(T)];
var metaData = new EventMetaData<T> { Callback = listener, EventFilter = eventFilter };
listeners.Add(metaData);
SendDiagnosticEvent(EventDiagnosticType.AddListener, typeof(T), null, listener.Method);
}
/// <summary>
/// Allows a subscriber to unsubscribe
/// </summary>
/// <typeparam name="T">type of event</typeparam>
/// <param name="listener">event listener</param>
public static void UnSubscribe<T>(SubscriberCallbackDelegate<T> listener) where T : class
{
if (!SubscriberList.ContainsKey(typeof(T))) return;
var listeners = SubscriberList[typeof(T)];
listeners.RemoveAll(p =>
{
return p is EventMetaData<T> eventMetaData && eventMetaData.Callback == listener;
});
SendDiagnosticEvent(EventDiagnosticType.RemoveListener, typeof(T), null, listener.Method);
}
/// <summary>
/// Clears all listeners
/// </summary>
public static void Clear()
{
SubscriberList.Clear();
SendDiagnosticEvent(EventDiagnosticType.RemoveListener, null, null, null);
}
/// <summary>
/// Add diagnostic listener
/// </summary>
/// <param name="listener">event listener</param>
public static void SubscribeToDiagnosticEvents(DiagnosticCallbackDelegate listener)
{
DiagnosticList.Add(listener);
SendDiagnosticEvent(EventDiagnosticType.AddListenerDiagnostic, null, null, listener.Method);
}
/// <summary>
/// Clear diagnostic event
/// </summary>
/// <param name="listener">listener to unsubscribe</param>
public static void UnSubscribeToDiagnosticEvents(DiagnosticCallbackDelegate listener)
{
DiagnosticList.RemoveAll(p => p == listener);
SendDiagnosticEvent(EventDiagnosticType.RemoveListenerDiagnostic, null, null, listener.Method);
}
/// <summary>
/// Clear all diagnostic events
/// </summary>
public static void ClearDiagnosticEvents()
{
DiagnosticList.Clear();
SendDiagnosticEvent(EventDiagnosticType.RemoveListenerDiagnostic, null, null, null);
}
/// <summary>
/// Sends a diagnostic event
/// </summary>
/// <param name="diagnosticEventType"></param>
/// <param name="eventType"></param>
/// <param name="eventData"></param>
/// <param name="listenerMethod"></param>
private static void SendDiagnosticEvent(EventDiagnosticType diagnosticEventType, Type eventType, object eventData, MemberInfo listenerMethod)
{
foreach (var diagnosticEvent in DiagnosticList)
{
string listener = null;
if (listenerMethod != null && listenerMethod.DeclaringType != null)
{
listener = $"{listenerMethod.DeclaringType.FullName}.{listenerMethod.Name} , {listenerMethod.DeclaringType.Assembly.FullName}";
}
diagnosticEvent(diagnosticEventType, eventType, eventData, listener);
}
}
}
public enum EventDiagnosticType
{
AddListener = 0,
AddListenerDiagnostic = 1,
PublishEvent = 2,
RemoveListenerDiagnostic = 3,
RemoveListener = 4
}
internal class EventMetaData<T> where T : class
{
public Predicate<T> EventFilter { get; set; }
public SubscriberCallbackDelegate<T> Callback { get; set; }
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>

View File

@@ -0,0 +1,20 @@
using System;
namespace DTS.Common.Core.ServiceManager
{
/// <summary>
/// Event fired when a service interface is published\unpublished
/// </summary>
public interface IServicePublishedEvent
{
/// <summary>
/// Returns type of service being published\unpublished
/// </summary>
Type ServiceType { get; }
/// <summary>
/// Returns true if Service is being published, false if being unpublished
/// </summary>
bool IsPublished { get; }
}
}

View File

@@ -0,0 +1,14 @@
using System.Xml.Serialization;
namespace DTS.Common.Core.PluginLib
{
/// <summary>
/// Support class that stores data de-serialized from DatPro.Core.PluginLib.Config in App.Config configuration section
/// </summary>
[XmlRoot(ElementName = "DatPro.Core.PluginLib.Config")]
public class PluginConfigData
{
[XmlArrayItem("Folder")]
public string[] PluginFolders;
}
}

View File

@@ -0,0 +1,27 @@
using DTS.Common.Core.Config;
// ReSharper disable InconsistentNaming
namespace DTS.Common.Core.PluginLib
{
/// <summary>
/// Static class used to store plugin configuration info
/// </summary>
public static class PluginConfig
{
/// <summary>
/// name of setting to look in config for equipment database
/// </summary>
public const string DTSPlugins = "DTSPlugins";
/// <summary>
/// concatenate plugin name from app setting with class exporter name for DTS plugins
/// </summary>
/// <param name="setting"></param>
/// <returns></returns>
public static string GetDTSPluginsSetting(string setting)
{
return DTSConfig.GetAppSetting(DTSPlugins) + "." + setting;
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
namespace DTS.Common.Core.ServiceManager
{
/// <summary>
/// Event fired when a service interface is published\unpublished
/// </summary>
public class ServicePublishedEvent : IServicePublishedEvent
{
/// <summary>
/// Returns type of service being published\unpublished
/// </summary>
public Type ServiceType
{
get;
internal set;
}
/// <summary>
/// Returns true if Service is being published, false if being unpublished
/// </summary>
public bool IsPublished
{
get;
internal set;
}
}
}

View File

@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
// ReSharper disable InconsistentNaming
namespace DTS.Common.Core.ServiceManager
{
/// <summary>
/// Service manager allows a component to publish an implementation of a singleton interface (service) that
/// other components can retrieve reference to without knowing who published it
/// </summary>
public static class ServiceManager
{
/// <summary>
/// Stores all published interfaces
/// </summary>
private static readonly Dictionary<Type, object> Services = new Dictionary<Type, object>();
/// <summary>
/// Publishes a service
/// </summary>
/// <typeparam name="T">interface type of service</typeparam>
/// <param name="item">implementation of service</param>
public static void Publish<T>(T item) where T : class
{
if (Services.ContainsKey(typeof(T)))
{
throw new ArgumentException($"{typeof(T).Name}: has already been published");
}
Services.Add(typeof(T), item);
SendServicePublishedEvent(typeof(T), true);
}
/// <summary>
/// Publishes a list of services
/// </summary>
/// <param name="item">class that will implement all the listed interfaces</param>
/// <param name="interfaceList">list of interfaces</param>
/// <param name="skipPublishedInterfaces">true to avoid errors on already published interfaces</param>
public static void Publish(object item, IEnumerable<Type> interfaceList, bool skipPublishedInterfaces)
{
foreach (var t in interfaceList)
{
if (Exists(t))
{
if (!skipPublishedInterfaces)
{
// service already published and caller indicated we should error out
throw new ArgumentException($"{t.Name}: has already been published");
}
}
else
{
// service doesn't exist so publish it
Services.Add(t, item);
SendServicePublishedEvent(t, true);
}
}
}
/// <summary>
/// Returns true if specifed interface has been published, false if not
/// </summary>
/// <typeparam name="T">type of interface to check</typeparam>
/// <returns>true if interface has been published; false if not published</returns>
public static bool Exists<T>() where T : class
{
return Services.ContainsKey(typeof(T));
}
/// <summary>
/// Returns true if specifed interface has been published, false if not
/// </summary>
/// <param name="t">type of interface to check</param>
/// <returns>true if interface has been published; false if not published</returns>
public static bool Exists(Type t)
{
return Services.ContainsKey(t);
}
/// <summary>
/// Returns published service
/// </summary>
/// <typeparam name="T">interface type of service</typeparam>
/// <returns>published service or exception if not currently published</returns>
public static T Get<T>() where T : class
{
if (!Services.ContainsKey(typeof(T)))
{
throw new ArgumentException($"{typeof(T).Name}: has not been published");
}
return Services[typeof(T)] as T;
}
/// <summary>
/// Clears specified service
/// </summary>
/// <typeparam name="T">interface type of service to clear</typeparam>
public static void Clear<T>() where T : class
{
if (Services.ContainsKey(typeof(T)))
{
SendServicePublishedEvent(typeof(T), false);
Services.Remove(typeof(T));
}
}
/// <summary>
/// Clears list of published interfaces
/// </summary>
/// <param name="interfaceList">list of interfaces to unpublish</param>
public static void Clear(IEnumerable<Type> interfaceList)
{
foreach (var t in interfaceList)
{
if (!Services.ContainsKey(t)) continue;
SendServicePublishedEvent(t, false);
Services.Remove(t);
}
}
/// <summary>
/// Sends a IServicePublishedEvent through the Event Manger to let subscribers know when
/// a service is published or unpublished
/// </summary>
/// <param name="type">type of interface</param>
/// <param name="published">true if being published, false if being unpublished</param>
private static void SendServicePublishedEvent(Type type, bool published)
{
EventManager.EventManager.Publish<IServicePublishedEvent>(new ServicePublishedEvent
{
IsPublished = published,
ServiceType = type
});
}
}
}

View File

@@ -0,0 +1,213 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace DTS.Common.Core.Settings
{
/// <summary>
/// Implements a dictionary with events when an item is changed
/// </summary>
/// <typeparam name="TKey">key type</typeparam>
/// <typeparam name="TItem">value type</typeparam>
public class SettingsCollection<TKey, TItem> : IDictionary<TKey, TItem>
{
/// <summary>
/// Event fired when an item changes in dictionary
/// </summary>
public event EventHandler<SettingsChangedEventArgs<TKey, TItem>> CollectionItemPropertyChanged;
/// <summary>
/// List of stored items
/// </summary>
private readonly Dictionary<TKey, TItem> _items = new Dictionary<TKey, TItem>();
#region IDictionary
/// <summary>
/// Add item
/// </summary>
/// <param name="key">key</param>
/// <param name="value">value</param>
public void Add(TKey key, TItem value)
{
_items.Add(key, value);
FireItemChangedEvent(ChangeSettingType.Add, key, value);
}
/// <summary>
/// Returns true if item contains key
/// </summary>
/// <param name="key">key</param>
/// <returns>true if key exists</returns>
public bool ContainsKey(TKey key)
{
return _items.ContainsKey(key);
}
/// <inheritdoc />
/// <summary>
/// Returns all keys in collection
/// </summary>
public ICollection<TKey> Keys => _items.Keys;
/// <summary>
/// Removes key
/// </summary>
/// <param name="key">key</param>
/// <returns>true if key removed</returns>
public bool Remove(TKey key)
{
var res = _items.Remove(key);
if (res) FireItemChangedEvent(ChangeSettingType.Remove, key);
return res;
}
/// <summary>
/// Trys to get specified value
/// </summary>
/// <param name="key">key</param>
/// <param name="value">value if key exists</param>
/// <returns>true if key exists; false if it doesn't exist</returns>
public bool TryGetValue(TKey key, out TItem value)
{
return _items.TryGetValue(key, out value);
}
/// <summary>
/// List of values in collection
/// </summary>
public ICollection<TItem> Values => _items.Values;
/// <summary>
/// Gets/sets item in collection (overloads [] operator)
/// </summary>
/// <param name="key">key</param>
/// <returns>item in collection</returns>
public TItem this[TKey key]
{
get => _items[key];
set
{
_items[key] = value;
FireItemChangedEvent(ChangeSettingType.Add, key, value);
}
}
/// <summary>
/// Adds new item
/// </summary>
/// <param name="item">new item to add</param>
public void Add(KeyValuePair<TKey, TItem> item)
{
_items.Add(item.Key, item.Value);
FireItemChangedEvent(ChangeSettingType.Add, item.Key, item.Value);
}
/// <summary>
/// Clears entire collection
/// </summary>
public void Clear()
{
_items.Clear();
FireItemChangedEvent(ChangeSettingType.ClearAll);
}
/// <summary>
/// Returns true if item is in collection
/// </summary>
/// <param name="item">item to check</param>
/// <returns>true if item exists</returns>
public bool Contains(KeyValuePair<TKey, TItem> item)
{
return _items.ContainsKey(item.Key) && _items.ContainsValue(item.Value);
}
/// <summary>
/// Copys items to array
/// </summary>
/// <param name="array"></param>
/// <param name="arrayIndex"></param>
public void CopyTo(KeyValuePair<TKey, TItem>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
/// <summary>
/// Returns count of items
/// </summary>
public int Count => _items.Count;
/// <summary>
/// Returns true if readonly collection
/// </summary>
public bool IsReadOnly => false;
/// <summary>
/// Removes specified item
/// </summary>
/// <param name="item">item to remove</param>
/// <returns>true if item removed</returns>
public bool Remove(KeyValuePair<TKey, TItem> item)
{
var res = _items.Remove(item.Key);
if (res) FireItemChangedEvent(ChangeSettingType.Remove, item.Key);
return res;
}
/// <summary>
/// Return enumerator for collection
/// </summary>
/// <returns>enumerator</returns>
public IEnumerator<KeyValuePair<TKey, TItem>> GetEnumerator()
{
return _items.GetEnumerator();
}
/// <summary>
/// Return enumerator for collection
/// </summary>
/// <returns>enumerator</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
#endregion
#region Private methods
/// <summary>
/// Fires collection change event
/// </summary>
/// <param name="changeType">type of change</param>
private void FireItemChangedEvent(ChangeSettingType changeType)
{
CollectionItemPropertyChanged?.Invoke(this, new SettingsChangedEventArgs<TKey, TItem>(changeType));
}
/// <summary>
/// Fires collection change event
/// </summary>
/// <param name="changeType">type of change</param>
/// <param name="key">key</param>
private void FireItemChangedEvent(ChangeSettingType changeType, TKey key)
{
CollectionItemPropertyChanged?.Invoke(this, new SettingsChangedEventArgs<TKey, TItem>(changeType, key));
}
/// <summary>
/// Fires collection change event
/// </summary>
/// <param name="changeType">type of change</param>
/// <param name="key">key</param>
/// <param name="item">item</param>
private void FireItemChangedEvent(ChangeSettingType changeType, TKey key, TItem item)
{
CollectionItemPropertyChanged?.Invoke(this, new SettingsChangedEventArgs<TKey, TItem>(changeType, key, item));
}
#endregion
}
}

View File

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{FAB1F470-1574-4301-B56E-D3364AA93679}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DTS.Common.Core</RootNamespace>
<AssemblyName>DTS.Common.Core</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SccProjectName>
</SccProjectName>
<SccLocalPath>
</SccLocalPath>
<SccAuxPath>
</SccAuxPath>
<SccProvider>
</SccProvider>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Config\DTSConfig.cs" />
<Compile Include="DTSConstants.cs" />
<Compile Include="EventManager\EventManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServiceManager\IServicePublishedEvent.cs" />
<Compile Include="ServiceManager\ServiceManager.cs" />
<Compile Include="ServiceManager\ServicePublishedEvent.cs" />
<Compile Include="Settings\SettingsChangedEventArgs.cs" />
<Compile Include="Settings\SettingsCollection.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="Design\DTS.Common.CoreClassDiagram.cd" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DTS.Common.Utilities\DTS.Common.Utilities.csproj">
<Project>{03EACE47-EA59-44AC-B49D-956E4DC4D618}</Project>
<Name>DTS.Common.Utilities</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,272 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.ReflectionModel;
using System.Linq;
using DTS.Common.Core.Config;
using DTS.Common.Utilities.Logging;
// ReSharper disable RedundantJumpStatement
// ReSharper disable UnusedTypeParameter
// ReSharper disable UnusedMember.Local
// ReSharper disable UnusedParameter.Local
namespace DTS.Common.Core.PluginLib
{
/// <summary>
/// PluginManager class to manage MEF plugins
/// NOTE: This class has to be thread safe
/// </summary>
public class PluginManager
{
// reference to singleton plugin manager
private static PluginManager _pluginManager;
/// <summary>
/// MEF catalog
/// </summary>
public AggregateCatalog PluginCatalog { get; set; }
/// <summary>
/// MEF container
/// </summary>
private CompositionContainer PluginContainer { get; set; }
/// <summary>
/// Thread lock
/// </summary>
private static readonly object THREAD_LOCK = new object();
/// <summary>
/// Constructor that intializes catalog and container
/// </summary>
private PluginManager(string appPath)
{
// Create MEF catalog
PluginCatalog = new AggregateCatalog();
DTSConfig.DTSConfigInit(appPath);
// Get plugin folder from App.Config;
var pcsh = (PluginConfigSectionHandler)DTSConfig.GetSection("DTS.Common.Core.PluginLib.Config"); //DTS.Common.Core.PluginLib.Config
if (pcsh == null)
{
APILogger.Log("## Unable to retrieve plugin config data from DTS.Config (DTS.Common.Core.PluginLib.Config)");
throw new Exception("Unable to retrieve plugin config data from DTS.config (DTS.Common.Core.PluginLib.Config)");
}
foreach (FilterHashElement element in pcsh.HashKeys)
{
try
{
var info = new DirectoryInfo(element.Value);
if (!info.Exists)
{
APILogger.Log($"## Plugin directory does not exist: {element.Value}");
throw new IOException($"Plugin directory does not exist: {element.Value}");
}
PluginCatalog.Catalogs.Add(new DirectoryCatalog(element.Value));
}
catch (Exception ex)
{
APILogger.Log(ex);
throw;
}
}
// Create MEF container
PluginContainer = new CompositionContainer(PluginCatalog);
foreach (var catalog in PluginCatalog.Catalogs)
{
var directoryCatalog = catalog as DirectoryCatalog;
if (directoryCatalog == null)
{
APILogger.Log("## directory catalog is null");
continue;
}
var pluginDir = directoryCatalog.FullPath;
var files = new DirectoryInfo(pluginDir).GetFiles("*.dll");
foreach (var assembly in files.Where(f => !f.Name.StartsWith("DTS.Common") && !f.Name.StartsWith("C1") && !f.Name.StartsWith("Xceed")))
{
var assemblyName = pluginDir + @"\" + assembly.Name;
//var assemblyName = $"{pluginDir}{assembly.Name}";
// verift assembly exists and matches requested paramaters
if (!File.Exists(assemblyName))
{
APILogger.Log($"## File Not Found!: {assemblyName}");
continue;
}
//var info = AssemblyName.GetAssemblyName(assemblyName);
// NOTE: sometimes args.Name only has short assembly name, sometimes full name with public key and version
//if (info == null || (info.FullName != args.Name && (info.Name != args.Name)))
//{
// continue;
//}
// Load assembly
try
{
var asm = Assembly.LoadFrom(assemblyName);
}
catch (Exception ex)
{
APILogger.Log("## Failed to load assembly:", ex);
throw;
}
}
}
}
public List<Assembly> GetPluginList<T>() where T : class
{
var assemblyList = new List<Assembly>();
var manager = GetPluginManager(string.Empty);
foreach (var catalog in manager.PluginCatalog.Catalogs)
{
var directoryCatalog = catalog as DirectoryCatalog;
if (directoryCatalog == null) continue;
assemblyList.AddRange(directoryCatalog.Parts.Select(part => ReflectionModelServices.GetPartType(part).Value.Assembly).Distinct().ToList());
}
return assemblyList;
}
/// <summary>
/// Returns MEF plugin of type t
/// Throws error if no type exported by plugin or more than one plugin has exported type
/// </summary>
/// <typeparam name="T">type to get</typeparam>
/// <returns>reference to type t from MEF plugin</returns>
public static T GetPlugin<T>() where T : class
{
var export = GetPluginManager(string.Empty).PluginContainer.GetExport<T>();
return export?.Value;
}
/// <summary>
/// Returns MEF plugin of type T from a collection of plugins that export the same type
/// </summary>
/// <typeparam name="T">exported type implemented by potentially multiple plugins</typeparam>
/// <param name="configPluginSetting">string that tells us which specific plugin we want</param>
/// <returns>type T</returns>
public static T GetPlugin<T>(string configPluginSetting) where T : class
{
var result = new Lazy<T>();
var manager = GetPluginManager(configPluginSetting);
var plugins = manager.PluginContainer.GetExports<T>();
//loop through plugins returned and get the specific one we're looking for
foreach (var item in plugins)
{
if (item.Value.ToString() != configPluginSetting) continue;
result = item;
break;
}
return result.Value;
}
/// <summary>
/// Returns list of MEF plugins that export type t
/// </summary>
/// <typeparam name="T">type to get</typeparam>
/// <returns>list of references to type t from MEF plugins</returns>
public static IEnumerable<Lazy<T>> GetPlugins<T>() where T : class
{
var manager = GetPluginManager(string.Empty);
return manager.PluginContainer.GetExports<T>();
}
/// <summary>
/// Returns static singleton reference to plugin manager class
/// </summary>
/// <returns>plugin class</returns>
public static PluginManager GetPluginManager(string appPath)
{
if (!string.IsNullOrWhiteSpace(appPath))
{
var directoryInfo = new DirectoryInfo(appPath);
}
lock (THREAD_LOCK)
{
if (_pluginManager != null)
{
foreach (var catalog in _pluginManager.PluginCatalog.Catalogs)
{
var directoryCatalog = catalog as DirectoryCatalog;
if (directoryCatalog == null) continue;
}
}
return _pluginManager ?? (_pluginManager = new PluginManager(appPath));
}
}
/// <summary>
/// This event triggered if system can't find plugin dependancies
/// CurrentDomain_AssemblyResolve can search through list of directories instead of just one
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <returns></returns>
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
foreach (var catalog in PluginCatalog.Catalogs)
{
var directoryCatalog = catalog as DirectoryCatalog;
if (directoryCatalog == null)
{
APILogger.Log("## null directoryCatalog");
continue;
}
var pluginDir = directoryCatalog.FullPath;
// get name of assembly
var baseName = args.Name.Split(new[] { ',' })[0];
var assemblyName = $"{pluginDir}\\{baseName}.dll";
// verift assembly exists and matches requested paramaters
if (!File.Exists(assemblyName))
{
APILogger.Log($"## assembly does not exist: {assemblyName}");
continue;
}
var info = AssemblyName.GetAssemblyName(assemblyName);
// NOTE: sometimes args.Name only has short assembly name, sometimes full name with public key and version
if (info == null || info.FullName != args.Name && info.Name != args.Name)
{
APILogger.Log("## assembly info is incomplete");
continue;
}
// Load assembly
try
{
var asm = Assembly.LoadFrom(assemblyName);
return asm;
}
catch (Exception ex)
{
APILogger.Log("## failed to load", ex);
throw;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("DTS.Common.Core")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DTS.Common.Core")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("bdf5ad7a-51db-4ad0-8186-d1ead7405848")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]