Dalibor Votruba 2 лет назад
Родитель
Сommit
b9167bc535
31 измененных файлов с 996 добавлено и 17 удалено
  1. 7 0
      Workbench/Processor/Dependencies/Data/ITypedArgument.cs
  2. 10 1
      Workbench/Processor/Dependencies/Data/TypedArgument.cs
  3. 37 0
      Workbench/Processor/Dependencies/Value/ValueConverter.cs
  4. 3 1
      Workbench/Processor/Program.cs
  5. 2 2
      Workbench/Processor/Providers/ProcessorStorageProvider.cs
  6. 6 2
      Workbench/Processor/Queue/ProcessQueue.cs
  7. 47 4
      Workbench/Processor/Task/ExecuteProvider/DummyExecuteProvider.cs
  8. 11 3
      Workbench/Processor/Task/ExecuteProvider/TaskExecuteProvider.cs
  9. 1 0
      Workbench/Processor/Task/ExecuteProvider/TaskExecuteProviderFactory.cs
  10. 2 1
      Workbench/Processor/Task/ITaskItemData.cs
  11. 3 3
      Workbench/Processor/Task/TaskItem.cs
  12. 9 0
      Workbench/QMonitor/qmonlib/Attributes/MonitoredClassAttribute.cs
  13. 11 0
      Workbench/QMonitor/qmonlib/Attributes/MonitoredPropertyAttribute.cs
  14. 19 0
      Workbench/QMonitor/qmonlib/Dependencies/Object/DisposableObject.cs
  15. 41 0
      Workbench/QMonitor/qmonlib/Dependencies/Object/Extensions/ObjectExt.cs
  16. 71 0
      Workbench/QMonitor/qmonlib/Dependencies/Object/Singleton.cs
  17. 32 0
      Workbench/QMonitor/qmonlib/Dependencies/Object/WeakReference.cs
  18. 73 0
      Workbench/QMonitor/qmonlib/Dependencies/Reflection/AttributeQuery.cs
  19. 33 0
      Workbench/QMonitor/qmonlib/Dependencies/Reflection/Extensions/ActivatorExt.cs
  20. 79 0
      Workbench/QMonitor/qmonlib/Dependencies/Reflection/Extensions/AssemblyExt.cs
  21. 26 0
      Workbench/QMonitor/qmonlib/Dependencies/Reflection/Extensions/AssemblyNameExt.cs
  22. 38 0
      Workbench/QMonitor/qmonlib/Dependencies/Reflection/Extensions/TypeExt.cs
  23. 126 0
      Workbench/QMonitor/qmonlib/Dependencies/Reflection/QualifiedName.cs
  24. 54 0
      Workbench/QMonitor/qmonlib/Dependencies/Reflection/TypeUtils.cs
  25. 6 0
      Workbench/QMonitor/qmonlib/Interfaces/IMonitored.cs
  26. 33 0
      Workbench/QMonitor/qmonlib/MonItemGeneral.cs
  27. 2 0
      Workbench/QMonitor/qmonlib/Program.cs
  28. 155 0
      Workbench/QMonitor/qmonlib/QMonClient.cs
  29. 24 0
      Workbench/QMonitor/qmonlib/QMonConfiuration.cs
  30. 10 0
      Workbench/QMonitor/qmonlib/qmonlib.csproj
  31. 25 0
      Workbench/QMonitor/qmonlib/qmonlib.sln

+ 7 - 0
Workbench/Processor/Dependencies/Data/ITypedArgument.cs

@@ -2,4 +2,11 @@
 
 public interface ITypedArgument
 {
+    string Name {get;}
+    string? Value { get; }
+    public Type ValueType { get; }
+    bool IsOutput { get; }
+
+    TValue? GetTypedValue<TValue>();
+
 }

+ 10 - 1
Workbench/Processor/Dependencies/Data/TypedArgument.cs

@@ -1,4 +1,6 @@
 using System;
+using System.Net.Http.Headers;
+using Quadarax.Foundation.Core.Value;
 
 namespace Quadarax.Foundation.Core.Data;
 
@@ -24,6 +26,13 @@ public class TypedArgument : ITypedArgument
     public TypedArgument(string name, object valueTyped, bool isOutput = false) : this(name, valueTyped?.ToString(), valueTyped?.GetType(), isOutput)
     {
          
-    }  
+    }
+
+    public TValue? GetTypedValue<TValue>()
+    {
+        if (Value == null) return default(TValue);
+        return ValueConverter.ConvertTo<TValue>(Value);
+    }
+
 }
    

+ 37 - 0
Workbench/Processor/Dependencies/Value/ValueConverter.cs

@@ -0,0 +1,37 @@
+namespace Quadarax.Foundation.Core.Value
+{
+    public static class ValueConverter
+    {
+
+        public static object ConvertTo(Type toType, string value)
+        {
+            if (string.IsNullOrEmpty(value)) return null;
+
+
+            if (toType == typeof(string)) return (object)value;
+            if (toType == typeof(int)) return (object)int.Parse(value);
+            if (toType == typeof(long)) return (object)long.Parse(value);
+            if (toType == typeof(bool)) return (object)bool.Parse(value);
+            if (toType == typeof(double)) return (object)double.Parse(value);
+            if (toType == typeof(float)) return (object)float.Parse(value);
+            if (toType == typeof(decimal)) return (object)decimal.Parse(value);
+            if (toType == typeof(byte)) return (object)byte.Parse(value);
+            if (toType == typeof(sbyte)) return (object)sbyte.Parse(value);
+            if (toType == typeof(short)) return (object)short.Parse(value);
+            if (toType == typeof(ushort)) return (object)ushort.Parse(value);
+            if (toType == typeof(uint)) return (object)uint.Parse(value);
+            if (toType == typeof(ulong)) return (object)ulong.Parse(value);
+            if (toType == typeof(char)) return (object)char.Parse(value);
+            if (toType == typeof(Guid)) return (object)Guid.Parse(value);
+            if (toType == typeof(TimeSpan)) return (object)TimeSpan.Parse(value);
+            if (toType.IsEnum) return (object)Enum.Parse(toType, value, true);
+            
+            throw new NotSupportedException($"Cannot convert value to type '{toType}'. Not supported!");
+        }
+
+        public static TValue? ConvertTo<TValue>(string value)
+        {
+            return (TValue)ConvertTo(typeof(TValue), value);
+        }
+    }
+}

+ 3 - 1
Workbench/Processor/Program.cs

@@ -2,6 +2,8 @@
 
 using Quadarax.Foundation.Core.Business.Processor.Providers;
 using Quadarax.Foundation.Core.Business.Processor.Queue;
+using Quadarax.Foundation.Core.Business.Processor.Task.ExecuteProvider;
+using Quadarax.Foundation.Core.Data;
 using Quadarax.Foundation.Core.Object;
 
 Console.WriteLine("Processor test bench ...");
@@ -12,7 +14,7 @@ using (var processor = new ProcessQueue("TEST", new InMemoryStorageProvider(), n
     for (int i = 0; i < 10; i++)
     {
         var item = processor.CreateTask($"task{i:D2}",
-            "test-class", Array.Empty<TypedArgument>(),
+            typeof(DummyExecuteProvider).AssemblyQualifiedName, DummyExecuteProvider.CreateDefaultArguments(),
             5,
             5,
             TimeSpan.FromSeconds(5),

+ 2 - 2
Workbench/Processor/Providers/ProcessorStorageProvider.cs

@@ -63,7 +63,7 @@ public abstract class ProcessorStorageProvider : DisposableObject, IProcessorSto
     public ITaskItemData[] Get(params ITaskIdentifier[] ids)
     {
         if (ids == null) throw new ArgumentNullException(nameof(ids));
-        if (ids.Length > 0) throw new ArgumentOutOfRangeException(nameof(ids), "must have at least one element in array.");
+        if (ids.Length == 0) throw new ArgumentOutOfRangeException(nameof(ids), "must have at least one element in array.");
         EnsureOpen();
         var result = OnGet(ids);
         Log(LogSeverityEnum.Debug, $"Gets {result.Length} items by {ids.Length} identifiers from storage.");
@@ -73,7 +73,7 @@ public abstract class ProcessorStorageProvider : DisposableObject, IProcessorSto
     public void Set(params ITaskItemData[] items)
     {
         if (items == null) throw new ArgumentNullException(nameof(items));
-        if (items.Length > 0) throw new ArgumentOutOfRangeException(nameof(items), "must have at least one element in array.");
+        if (items.Length == 0) throw new ArgumentOutOfRangeException(nameof(items), "must have at least one element in array.");
         EnsureOpen();
         var storedItems = OnGet(items.Select(x => x as ITaskIdentifier).ToArray());
         var cntUpdated = 0;

+ 6 - 2
Workbench/Processor/Queue/ProcessQueue.cs

@@ -91,7 +91,7 @@ namespace Quadarax.Foundation.Core.Business.Processor.Queue
             IsRunning = false;
         }
 
-        public ITaskIdentifier CreateTask(string reference, string providerClass, TypedArgument[] initialArguments, byte priority, int initialAttempts, TimeSpan attemptTimeout, DateTime validFrom, DateTime? validTo)
+        public ITaskIdentifier CreateTask(string reference, string providerClass, ITypedArgument[] initialArguments, byte priority, int initialAttempts, TimeSpan attemptTimeout, DateTime validFrom, DateTime? validTo)
         {
             try
             {
@@ -117,13 +117,17 @@ namespace Quadarax.Foundation.Core.Business.Processor.Queue
 
         public ITaskItem[] GetTask(ITaskIdentifier[] ids)
         {
+            if (ids == null) throw new ArgumentNullException(nameof(ids));
+
+            if (!ids.Any()) return Array.Empty<ITaskItem>();
+
             var result = _storageProvider.Get(ids);
             var diff = result.Select(x => x.Id).CompareDiff(ids.Select(x => x.Id));
 
             if (diff.Any())
                 throw new Exception($"Tasks [{string.Join(",", diff)}] not found!");
 
-            return (ITaskItem[])result;
+            return result.Select(x=>x as ITaskItem).ToArray()!;
         }
 
         public ITaskIdentifier[] QueryTasks(IPaging paging,ProcessStatusEnum[] states,int maxItems, bool affinityQueue = true, bool onlyTimeValid = true, bool includeDeleted = false)

+ 47 - 4
Workbench/Processor/Task/ExecuteProvider/DummyExecuteProvider.cs

@@ -7,19 +7,62 @@ namespace Quadarax.Foundation.Core.Business.Processor.Task.ExecuteProvider
         #region *** Constants ***
         public const string EP_DMY_DURATION = "duration";
         public const string EP_DMY_RESULT = "result";
-        public const string EP_DMY_ERROR = "error-emit";
+        public const string EP_DMY_ERROR = "is-error-emit";
         #endregion  
 
+        #region *** Properties ***
+        private TimeSpan Duration { get; set; }
+        private ResultType Result { get; set; }
+        private bool IsErrorEmit { get; set; }
+        #endregion
 
         #region *** Overrides ***
-        protected override void OnExecute(ITaskIdentifier taskIdentifier, ITypedArgument[] arguments, ITaskExecuteResult result)
+        protected override void OnExecute(ITaskIdentifier taskIdentifier, ITaskExecuteResult result)
         {
-            throw new NotImplementedException();
+            if (IsErrorEmit) throw new Exception("Dummy exception emitted.");
+
+
+            switch (Result)
+            {
+                case ResultType.Random:
+                    ((Result)result).SetSuccess(new Random().Next(0, 2) == 1);
+                    break;
+                case ResultType.Success:
+                    ((Result)result).SetSuccess(true);
+                    break;
+                case ResultType.Fail:
+                    ((Result)result).SetSuccess(false);
+                    break;
+            }
         }
 
         protected override void OnValidateArguments(ITypedArgument[] arguments)
         {
-            throw new NotImplementedException();
+            Duration = GetArgument<TimeSpan>(arguments, EP_DMY_DURATION);
+            Result = GetArgument<ResultType>(arguments, EP_DMY_RESULT);
+            IsErrorEmit = GetArgument<bool>(arguments, EP_DMY_ERROR);
+        }
+        #endregion
+
+        #region *** Public Operations ***
+        public static ITypedArgument[] CreateDefaultArguments()
+        {
+            var result = new List<ITypedArgument>();
+            result.Add(new TypedArgument(EP_DMY_DURATION, typeof(TimeSpan), false));
+            result.Add(new TypedArgument(EP_DMY_RESULT, typeof(ResultType), false));
+            result.Add(new TypedArgument(EP_DMY_ERROR, typeof(bool), false));
+            return result.ToArray();
+        }
+        #endregion
+
+
+        #region *** Nested Classes ***
+
+        private enum ResultType
+        {
+            Random,
+            Success,
+            Fail
         }
         #endregion
     }

+ 11 - 3
Workbench/Processor/Task/ExecuteProvider/TaskExecuteProvider.cs

@@ -30,7 +30,7 @@ namespace Quadarax.Foundation.Core.Business.Processor.Task.ExecuteProvider
             try
             {
                 OnValidateArguments(arguments);
-                OnExecute(taskIdentifier, arguments, result);
+                OnExecute(taskIdentifier, result);
             }
             catch (Exception e)
             {
@@ -48,10 +48,18 @@ namespace Quadarax.Foundation.Core.Business.Processor.Task.ExecuteProvider
             return result;
         }
 
-        protected abstract void OnExecute(ITaskIdentifier taskIdentifier, ITypedArgument[] arguments,
-            ITaskExecuteResult result);
+        protected abstract void OnExecute(ITaskIdentifier taskIdentifier, ITaskExecuteResult result);
         protected abstract void OnValidateArguments(ITypedArgument[] arguments);
 
+        protected TValue? GetArgument<TValue>(ITypedArgument[] arguments, string argumentName)
+        {
+            if (arguments == null || arguments.Length == 0) throw new ArgumentNullException(nameof(arguments));
+
+            var result = arguments.FirstOrDefault(x => string.Equals(x.Name, argumentName, StringComparison.InvariantCultureIgnoreCase));
+            if (result == null) throw new ArgumentException($"Argument '{argumentName}' not found!");
+
+            return result.GetTypedValue<TValue>();
+        }
 
         #region *** Nested classes ***
         private class TaskExecuteResult : Result, ITaskExecuteResult

+ 1 - 0
Workbench/Processor/Task/ExecuteProvider/TaskExecuteProviderFactory.cs

@@ -1,4 +1,5 @@
 using System.Reflection;
+using Quadarax.Foundation.Core.Data;
 using Quadarax.Foundation.Core.Object;
 using Quadarax.Foundation.Core.Reflection;
 

+ 2 - 1
Workbench/Processor/Task/ITaskItemData.cs

@@ -1,4 +1,5 @@
 using Quadarax.Foundation.Core.Business.Processor.Enums;
+using Quadarax.Foundation.Core.Data;
 using Quadarax.Foundation.Core.Object;
 
 namespace Quadarax.Foundation.Core.Business.Processor.Task
@@ -67,7 +68,7 @@ namespace Quadarax.Foundation.Core.Business.Processor.Task
         /// <summary>
         /// Collection of task arguments.
         /// </summary>
-        public TypedArgument[] Arguments { get; }
+        public ITypedArgument[] Arguments { get; }
         #endregion
     }
 }

+ 3 - 3
Workbench/Processor/Task/TaskItem.cs

@@ -88,12 +88,12 @@ namespace Quadarax.Foundation.Core.Business.Processor.Task
         /// </summary>
         public ITaskStatusLogItem[] StatusLog => _statusLog.ToArray();
 
-        public TypedArgument[] Arguments => _arguments.ToArray(); 
+        public ITypedArgument[] Arguments => _arguments.ToArray(); 
         #endregion
 
         #region *** Private Fields ***
         private readonly List<ITaskStatusLogItem> _statusLog = new();
-        private readonly List<TypedArgument> _arguments = new();
+        private readonly List<ITypedArgument> _arguments = new();
         private ILog _log;
         private ProcessQueue _parent;
         private Thread _thread;
@@ -107,7 +107,7 @@ namespace Quadarax.Foundation.Core.Business.Processor.Task
             Id = id;
             SetStatus(initialStatus, "(System) Initialization");
         }
-        public TaskItem(ProcessQueue parent, string reference, string providerClass, TypedArgument[] initialArguments, byte priority, int attepts, TimeSpan? attemptTimeout, DateTime validFrom, DateTime? validTo, ITaskStatusLogItem[]? initialStatusLog)
+        public TaskItem(ProcessQueue parent, string reference, string providerClass, ITypedArgument[] initialArguments, byte priority, int attepts, TimeSpan? attemptTimeout, DateTime validFrom, DateTime? validTo, ITaskStatusLogItem[]? initialStatusLog)
         {	
             AffinityToken = null;
             _parent = parent ?? throw new ArgumentNullException(nameof(parent)); 

+ 9 - 0
Workbench/QMonitor/qmonlib/Attributes/MonitoredClassAttribute.cs

@@ -0,0 +1,9 @@
+namespace qmonlib.Attributes
+{
+    [AttributeUsage(AttributeTargets.Class)]
+    public class MonitoredClassAttribute : Attribute
+    {
+        public string Caption { get; set; }
+        public string Description { get; set; }
+    }
+}

+ 11 - 0
Workbench/QMonitor/qmonlib/Attributes/MonitoredPropertyAttribute.cs

@@ -0,0 +1,11 @@
+namespace qmonlib.Attributes
+{
+    [AttributeUsage(AttributeTargets.Property)]
+    public class MonitoredPropertyAttribute : Attribute
+    {
+        public string Name { get; set; }
+        public string Label { get; set; }
+        public string Description { get; set; }
+        public int Order { get; set; }
+    }
+}

+ 19 - 0
Workbench/QMonitor/qmonlib/Dependencies/Object/DisposableObject.cs

@@ -0,0 +1,19 @@
+using System;
+
+namespace Quadarax.Foundation.Core.Object
+{
+    public abstract class DisposableObject : IDisposable
+    {
+        private bool _isDisposing;
+
+        public void Dispose()
+        {
+            if (_isDisposing)
+                return;
+            _isDisposing = true;
+            OnDisposing();
+        }
+
+        protected abstract void OnDisposing();
+    }
+}

+ 41 - 0
Workbench/QMonitor/qmonlib/Dependencies/Object/Extensions/ObjectExt.cs

@@ -0,0 +1,41 @@
+using System.Collections;
+using Quadarax.Foundation.Core.Reflection.Extensions;
+
+namespace Quadarax.Foundation.Core.Object.Extensions
+{
+    public static class ObjectExt
+    {
+        public static IDictionary<string, object?> GetAllPropertyValues(this object? obj,string? indentPrefix = null)
+        {
+            var props = new Dictionary<string, object?>();
+            if (obj == null)
+                return props;
+
+            if (string.IsNullOrEmpty(indentPrefix))
+                indentPrefix = string.Empty;
+
+            var type = obj.GetType();
+            var propList = type.GetAllProperties();
+            foreach (var prop in propList)
+            {
+                var val = prop.GetValue(obj, new object[] { });
+                if (val is IEnumerable enumerable)
+                {
+                    var cnt = 0;
+                    foreach (var item in enumerable)
+                    {
+                        var itemProps = item.GetAllPropertyValues(prop.Name + $"[{cnt}].");
+                        foreach (var ip in itemProps)
+                            props.Add(ip.Key, ip.Value);
+                        cnt++;
+                    }
+                }
+                else
+                    props.Add(indentPrefix + prop.Name, val);
+            }
+ 
+            return props;
+        }
+
+    }
+}

+ 71 - 0
Workbench/QMonitor/qmonlib/Dependencies/Object/Singleton.cs

@@ -0,0 +1,71 @@
+namespace Quadarax.Foundation.Core.Object
+{
+  
+        /// <summary>
+        /// Well-known singleton pattern implementation.
+        /// </summary>
+        /// <remarks>
+        /// The singleton type must inherit this class. The actual instance type is determined
+        /// by the only type parameter. The singleton type must implement a default constructor.
+        /// </remarks>
+        /// <typeparam name="TInstance"></typeparam>
+        public class Singleton<TInstance> where TInstance : new()
+        {
+            private static TInstance? _instance;
+            private static readonly object _syncRoot = new object();
+
+            /// <summary>
+            /// Singleton instance
+            /// </summary>
+            public static TInstance Instance
+            {
+                get
+                {
+                    if (_instance == null)
+                    {
+                        lock (_syncRoot)
+                        {
+                            if (_instance == null)
+                                _instance = new TInstance();
+                        }
+                    }
+
+                    return _instance;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Well-known singleton pattern implementation.
+        /// </summary>
+        /// <remarks>
+        /// The singleton type must inherit this class. The actual instance type is determined
+        /// by the only type parameter. The singleton type must implement a default constructor.
+        /// </remarks>
+        /// <typeparam name="TInstance"></typeparam>
+        /// <typeparam name="TInterface"></typeparam>
+        public class Singleton<TInterface, TInstance>
+            where TInstance : TInterface, new()
+            where TInterface : class
+        {
+            private static volatile TInterface? _instance;
+            private static readonly object _syncRoot = new object();
+
+            public static TInterface Instance
+            {
+                get
+                {
+                    if (_instance == null)
+                    {
+                        lock (_syncRoot)
+                        {
+                            if (_instance == null)
+                                _instance = new TInstance();
+                        }
+                    }
+
+                    return _instance;
+                }
+            }
+        }
+}

+ 32 - 0
Workbench/QMonitor/qmonlib/Dependencies/Object/WeakReference.cs

@@ -0,0 +1,32 @@
+using System;
+
+namespace Quadarax.Foundation.Core.Object
+{
+    /// <summary>
+    /// Strongly typed version of <see cref="WeakReference"/>.
+    /// </summary>
+    /// <typeparam name="TReference"></typeparam>
+    public class WeakReference<TReference> : WeakReference 
+        where TReference : class
+    {
+        public WeakReference(TReference target)
+            : base(target)
+        {
+        }
+
+        public WeakReference(TReference target, bool trackResurrection)
+            : base(target, trackResurrection)
+        {
+        }
+
+        /// <summary>
+        /// Strongly typed version of <see cref="WeakReference.Target"/>.
+        /// </summary>
+        public new TReference? Target
+        {
+            get { return base.Target as TReference; }
+            set { base.Target = value; }
+        }
+    }
+
+}

+ 73 - 0
Workbench/QMonitor/qmonlib/Dependencies/Reflection/AttributeQuery.cs

@@ -0,0 +1,73 @@
+using System.Reflection;
+
+namespace Quadarax.Foundation.Core.Reflection
+{
+    public static class AttributeQuery<TAttributeType> where TAttributeType : Attribute
+    {
+        public static TAttributeType Get<TAttributeProvider>(TAttributeProvider oProvider)
+            where TAttributeProvider : ICustomAttributeProvider
+        {
+            return Get(oProvider, true);
+        }
+
+        public static TAttributeType Get<TAttributeProvider>(TAttributeProvider oProvider, bool bInherit)
+            where TAttributeProvider : ICustomAttributeProvider
+        {
+            var attribute = Find(oProvider, bInherit);
+            if (attribute == null)
+            {
+                throw new Exception($"Cannot find attribute '{typeof(TAttributeType).Name}' inside class '{oProvider.GetType().AssemblyQualifiedName}'");
+            }
+            return attribute;
+        }
+
+        public static TAttributeType? Find<TAttributeProvider>(TAttributeProvider oProvider, bool bInherit)
+            where TAttributeProvider : ICustomAttributeProvider
+        {
+            var attributes = FindAll(oProvider, bInherit);
+            return attributes.Length > 0 ? attributes[0] : null;
+        }
+
+        public static TAttributeType?[] FindAll<TAttributeProvider>(TAttributeProvider oProvider)
+            where TAttributeProvider : ICustomAttributeProvider
+        {
+            return FindAll(oProvider, true);
+        }
+
+        public static TAttributeType?[] FindAll<TAttributeProvider>(TAttributeProvider oProvider, bool bInherit)
+            where TAttributeProvider : ICustomAttributeProvider
+        {
+            return (TAttributeType?[])oProvider.GetCustomAttributes(typeof(TAttributeType), bInherit);
+        }
+
+        public static MemberInfo[] GetMembers(Type oSource, BindingFlags eFlags)
+        {
+            var oResult = new List<MemberInfo>();
+            if (oSource.IsEnum)
+            {
+                var oMembers = oSource.GetEnumValues();
+                foreach (var oMember in oMembers)
+                {
+                    var oMemberInfo = oSource.GetMember(oMember.ToString() ?? string.Empty);                                        
+                    if (Has(oMemberInfo[0]))
+                        oResult.Add(oMemberInfo[0]);                    
+                }
+                return oResult.ToArray();
+            }
+            throw new NotSupportedException(
+                $"Attribute query in GetMembers does't support type object '{oSource.AssemblyQualifiedName}");
+        }
+
+        public static bool Has<TAttributeProvider>(TAttributeProvider oProvider)
+            where TAttributeProvider : ICustomAttributeProvider
+        {
+            return oProvider.IsDefined(typeof(TAttributeType), true);
+        }
+
+        public static bool Has<TAttributeProvider>(TAttributeProvider oProvider, bool bInherit)
+            where TAttributeProvider : ICustomAttributeProvider
+        {
+            return Find(oProvider, bInherit) != null;
+        }
+    }
+}

+ 33 - 0
Workbench/QMonitor/qmonlib/Dependencies/Reflection/Extensions/ActivatorExt.cs

@@ -0,0 +1,33 @@
+using System;
+
+namespace Quadarax.Foundation.Core.Reflection.Extensions
+{
+    public static class ActivatorExt
+    {
+        #region *** Private Fields ***
+        #endregion
+        #region *** Public Properties ***
+        #endregion
+        #region *** Constructors ***
+        #endregion
+        #region *** Public operations & overrides ***
+        public static object? CreateInstance(string sClassOrInterfaceQualifiedName, bool bLoadAssemblyIfNeeded, params object[] aConstructorParams)
+        {
+            var oType = Type.GetType(sClassOrInterfaceQualifiedName, false) ?? TypeUtils.GetRemoteType(sClassOrInterfaceQualifiedName);
+            if (oType == null) return null;
+
+            if (oType.IsInterface)
+            {
+                throw new ArgumentException($"Input class name '{sClassOrInterfaceQualifiedName}' cannot be an interface.",nameof(sClassOrInterfaceQualifiedName));
+            }
+            return Activator.CreateInstance(oType, aConstructorParams);
+        }
+        public static T CreateInstance<T>(string sClassOrInterfaceQualifiedName, bool bLoadAssemblyIfNeeded, params object[] aConstructorParams)
+        {
+            return (T) CreateInstance(sClassOrInterfaceQualifiedName, bLoadAssemblyIfNeeded, aConstructorParams)!;
+        }
+        #endregion
+        #region *** Private operations & overrides ***
+        #endregion        
+    }
+}

+ 79 - 0
Workbench/QMonitor/qmonlib/Dependencies/Reflection/Extensions/AssemblyExt.cs

@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+namespace Quadarax.Foundation.Core.Reflection.Extensions
+{
+    public static class AssemblyExt
+    {
+        #region *** Private Fields ***
+        #endregion
+        #region *** Public Properties ***
+        #endregion
+        #region *** Constructors ***
+        #endregion
+        #region *** Public operations & overrides ***
+        public static Version? GetVersion(this Assembly oAssembly)
+        {
+            if (oAssembly == null)
+                throw new ArgumentNullException(nameof(oAssembly));
+
+            return oAssembly.GetName().Version;
+        }
+        public static string GetDescription(this Assembly oAssembly)
+        {
+            var oAttribute = oAssembly.GetCustomAttributes(typeof(AssemblyDescriptionAttribute), true).FirstOrDefault();
+            if (oAttribute == null)
+                return string.Empty;
+            return ((AssemblyDescriptionAttribute)oAttribute).Description;
+        }
+        public static string GetProduct(this Assembly oAssembly)
+        {
+            var oAttribute = oAssembly.GetCustomAttributes(typeof(AssemblyProductAttribute), true).FirstOrDefault();
+            if (oAttribute == null)
+                return string.Empty;
+            return ((AssemblyProductAttribute)oAttribute).Product;
+        }
+        public static string GetCompany(this Assembly oAssembly)
+        {
+            var oAttribute = oAssembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), true).FirstOrDefault();
+            if (oAttribute == null)
+                return string.Empty;
+            return ((AssemblyCompanyAttribute)oAttribute).Company;
+        }
+        public static string GetCopyright(this Assembly oAssembly)
+        {
+            var oAttribute = oAssembly.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), true).FirstOrDefault();
+            if (oAttribute == null)
+                return string.Empty;
+            return ((AssemblyCopyrightAttribute)oAttribute).Copyright;
+        }
+        public static string GetTrademark(this Assembly oAssembly)
+        {
+            var oAttribute = oAssembly.GetCustomAttributes(typeof(AssemblyTrademarkAttribute), true).FirstOrDefault();
+            if (oAttribute == null)
+                return string.Empty;
+            return ((AssemblyTrademarkAttribute)oAttribute).Trademark;
+        }
+        public static string GetAssemblyPath(this Assembly oAssembly)
+        {
+            if (oAssembly == null)
+                throw new ArgumentNullException(nameof(oAssembly));
+
+            return Path.GetDirectoryName(new Uri(oAssembly.Location)?.LocalPath) + Path.DirectorySeparatorChar;
+        }
+        public static IEnumerable<Type> GetTypesWithAttribute<TAttribute>(this Assembly oAssembly, bool bInherit)where TAttribute : System.Attribute
+        {
+            var types = oAssembly.GetTypes();
+            return types.Where(x => x.IsDefined(typeof(TAttribute), bInherit)).ToArray();
+        }
+
+        #endregion
+        #region *** Private operations & overrides ***
+        #endregion
+
+
+    }
+}

+ 26 - 0
Workbench/QMonitor/qmonlib/Dependencies/Reflection/Extensions/AssemblyNameExt.cs

@@ -0,0 +1,26 @@
+using System.Reflection;
+using System.Text;
+
+namespace Quadarax.Foundation.Core.Reflection.Extensions
+{
+    public static class AssemblyNameExt
+    {
+        #region *** Private Fields ***
+		#endregion
+		
+		#region *** Public operations & overrides ***
+        public static string GetPublicKeyTokenString(this AssemblyName oOwner)
+        {            
+            var aPkToken = oOwner.GetPublicKeyToken();
+            if (aPkToken == null)
+                return string.Empty;
+            var oSb = new StringBuilder();
+            foreach (var nPk in aPkToken)
+                oSb.AppendFormat("{0:x2}", nPk);
+            return oSb.ToString();
+        }
+		#endregion
+		#region *** Private operations & overrides ***
+		#endregion        
+    }
+}

+ 38 - 0
Workbench/QMonitor/qmonlib/Dependencies/Reflection/Extensions/TypeExt.cs

@@ -0,0 +1,38 @@
+using System.Reflection;
+
+namespace Quadarax.Foundation.Core.Reflection.Extensions
+{
+    public static class TypeExt
+    {
+        public static IEnumerable<MethodInfo> GetMethodsWithAttribute<TAttribute>(this Type type, bool bInherit)where TAttribute : System.Attribute
+        {
+            var methods = type.GetMethods();
+            return methods.Where(x => x.IsDefined(typeof(TAttribute), bInherit)).ToArray();
+        }
+
+        public static IEnumerable<PropertyInfo> GetPropertiesWithAttribute<TAttribute>(this Type type, bool bInherit)where TAttribute : System.Attribute
+        {
+            var properties = type.GetProperties();
+            return properties.Where(x => x.IsDefined(typeof(TAttribute), bInherit)).ToArray();
+        }
+
+        public static IList<PropertyInfo> GetAllProperties(this Type? type)
+        {
+            if (type == null)
+                return new List<PropertyInfo>();
+
+            var propList = new List<PropertyInfo>(type.GetProperties(BindingFlags.Instance | BindingFlags.Public)); //type.GetTypeInfo().DeclaredProperties;
+            if (type.GetTypeInfo().BaseType != null)
+            {
+                var nestedProps = GetAllProperties(type.GetTypeInfo().BaseType);
+                foreach (var prop in nestedProps)
+                {
+                    if (propList.Any(x=>x.Name == prop.Name)) continue;
+                    propList.Add(prop);
+                }
+            }
+            return propList;
+        }
+
+    }
+}

+ 126 - 0
Workbench/QMonitor/qmonlib/Dependencies/Reflection/QualifiedName.cs

@@ -0,0 +1,126 @@
+using System.Reflection;
+using Quadarax.Foundation.Core.Reflection.Extensions;
+
+namespace Quadarax.Foundation.Core.Reflection
+{
+    public class QualifiedName
+    {
+        #region *** Constants ***
+        private const char CC_SEPARATOR = ',';
+        #endregion
+
+        #region *** Public Properties ***
+        // ReSharper disable MemberCanBePrivate.Global
+        // ReSharper disable UnusedAutoPropertyAccessor.Global
+        public string Name { get; private set; }
+        public string? Assembly { get; private set; }
+        public string? Culture { get; private set; }
+        public string Token { get; private set; }
+        public string Version { get; private set; }
+        public string AssemblyFullName { get; private set; }
+        // ReSharper restore UnusedAutoPropertyAccessor.Global
+        // ReSharper restore MemberCanBePrivate.Global
+        #endregion
+        #region *** Constructors ***
+        public QualifiedName(string sReflectionQualifiedName)
+        {
+            Name = string.Empty;
+            Token = string.Empty;
+            Version = string.Empty;
+            AssemblyFullName = string.Empty;
+            var aParts = sReflectionQualifiedName.Split(CC_SEPARATOR)
+                            .Select(x => x.Trim())
+                            .ToList();
+
+            if (aParts.Count==0)
+                return;
+            if (aParts.Count==1)
+            {
+                //jedna se o bud o codebase nebo assembly
+                bool bCodebase = false;
+                try
+                {
+                    var oType = Type.GetType(aParts[0].Trim(), true);
+                    if (oType == null)
+                        throw new Exception("Type is null");
+                    SetupProperties(oType.Assembly.GetName());
+                    bCodebase = true;
+                }
+                catch{}
+                if (bCodebase)
+                {
+                    //codebase
+                    Name = aParts[0].Trim();
+                }
+                else
+                {
+                    //assembly
+                    try
+                    {                        
+                        SetupProperties(new AssemblyName(aParts[0].Trim()));
+                    }
+                    catch (System.Exception e)
+                    {
+                        ThrowCannotParse(sReflectionQualifiedName, e);
+                    }
+                }
+
+            }
+            if (aParts.Count > 1)
+            {
+                //jedna se bud o codebase, assembly nebo assembly
+                try
+                {
+                    if (aParts[1].IndexOf('=') > 0)
+                    {
+                        //assembly
+                        SetupProperties(new AssemblyName(string.Join(CC_SEPARATOR.ToString() , aParts)));
+                    }
+                    else
+                    {
+                        //codebase, assembly
+                        SetupProperties(new AssemblyName(string.Join(CC_SEPARATOR.ToString(), aParts.Skip(1))));
+                        Name = aParts[0].Trim();
+                    }
+                }
+                catch (System.Exception e)
+                {
+                    ThrowCannotParse(sReflectionQualifiedName, e);
+                }                
+            }
+            
+
+            
+        }
+        #endregion
+        #region *** Public Operations ***
+        public string GetQualifiedNameWithoutVersion()
+        {
+            return Name + CC_SEPARATOR + " " + Assembly;
+        }
+
+        public bool LiteEquals(QualifiedName oCompareObject)
+        {
+            return oCompareObject.Assembly == Assembly && oCompareObject.Name == Name;
+        }
+        #endregion
+        #region *** Private Operations ***
+        private void SetupProperties(AssemblyName? oName)
+        {
+            if (oName == null)
+                throw new ArgumentNullException(nameof(oName));
+
+            Assembly = oName.Name;
+            AssemblyFullName = oName.FullName;
+            Culture = oName.CultureName;
+            Token = oName.GetPublicKeyTokenString();
+            Version = oName.Version!=null ? oName.Version.ToString() : string.Empty;            
+        
+        }
+        private void ThrowCannotParse(string sReflectionQualifiedName, System.Exception oInnerException)
+        {
+            throw new ArgumentException($"Cannot parse QualifiedName '{sReflectionQualifiedName}'!", oInnerException);
+        }
+        #endregion
+    }
+}

+ 54 - 0
Workbench/QMonitor/qmonlib/Dependencies/Reflection/TypeUtils.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace Quadarax.Foundation.Core.Reflection
+{
+    public static class TypeUtils
+    {
+
+        #region *** Private Fields ***
+        private static readonly IDictionary<string, Type?> m_oRemTypeCache = new Dictionary<string, Type?>();
+        // ReSharper disable InconsistentNaming
+        private static readonly object @lock;
+        // ReSharper restore InconsistentNaming
+        #endregion
+        #region *** Public Properties ***
+        #endregion
+        #region *** Constructors ***
+        static TypeUtils()
+        {
+            @lock = new object();
+        }
+        #endregion
+        #region *** Public operations & overrides ***
+        public static Type? GetRemoteType(string sReflectionQualifiedName)
+        {
+            if (m_oRemTypeCache.TryGetValue(sReflectionQualifiedName, out var type))
+                lock (@lock)
+                {
+                    return type;
+                }
+
+            var oName = new QualifiedName(sReflectionQualifiedName);
+            Type? oType;
+            if (oName.Assembly == null)
+            {
+                oType = Type.GetType(oName.Name, true);
+            }
+            else
+            {
+                var oAssembly = Assembly.Load(oName.Assembly);
+                oType = oAssembly.GetType(oName.Name, true);
+            }
+            lock (@lock)
+            {
+                m_oRemTypeCache.Add(sReflectionQualifiedName, oType);
+            }
+            return oType;
+        }
+        #endregion
+        #region *** Private operations & overrides ***
+        #endregion               
+    }
+}

+ 6 - 0
Workbench/QMonitor/qmonlib/Interfaces/IMonitored.cs

@@ -0,0 +1,6 @@
+namespace qmonlib.Interfaces
+{
+    public interface IMonitored
+    {
+    }
+}

+ 33 - 0
Workbench/QMonitor/qmonlib/MonItemGeneral.cs

@@ -0,0 +1,33 @@
+using System.Reflection;
+
+namespace qmonlib
+{
+    [Serializable]
+    public class MonItemGeneral
+    {
+        public string InstanceIdentifier { get; set; }
+        public string Caption { get; set; }
+        public string Description { get; set; }
+        public MonViewType ViewType { get; set; }
+
+        public List<ItemDescriptor> Items { get; set; } = new List<ItemDescriptor>();
+
+        public class ItemDescriptor
+        {
+            public string Name { get; set; }
+            public string TypeName { get; set; }
+            public string Label { get; set; }
+            public string Description { get; set; }
+            public int Order { get; set; }
+
+            [NonSerialized] public PropertyInfo? _propertyInfo;
+
+        }
+
+        public enum MonViewType
+        {
+            Item,
+            List
+        }
+    }
+}

+ 2 - 0
Workbench/QMonitor/qmonlib/Program.cs

@@ -0,0 +1,2 @@
+// See https://aka.ms/new-console-template for more information
+Console.WriteLine("Hello, World!");

+ 155 - 0
Workbench/QMonitor/qmonlib/QMonClient.cs

@@ -0,0 +1,155 @@
+using System.Net;
+using System.Net.Sockets;
+using System.Reflection;
+using qmonlib.Attributes;
+using Quadarax.Foundation.Core.Object;
+using Quadarax.Foundation.Core.Reflection.Extensions;
+
+namespace qmonlib
+{
+    public class QMonClient : DisposableObject
+    {
+        #region *** Properties ***
+        public bool IsEnabled
+        {
+            get => _isEnabled;
+            set
+            {
+                SetEnabled(value);
+            }
+        }
+        public string InstanceIdentifier { get; }
+        #endregion
+
+        #region *** Fields ***
+        private bool _isEnabled;
+        private QMonConfiuration _configuration;
+        private UdpClient? _udpClientGeneral;
+        private UdpClient? _udpClientData;
+
+        private Timer? _timerGeneral;
+        private Timer? _timerData;
+
+        private IDictionary<string, MonItemGeneral> _generalCache = new Dictionary<string, MonItemGeneral>();
+        #endregion
+
+        #region *** Constructors ***
+        public   QMonClient(string instanceIdentifier, QMonConfiuration configuration)     
+        {
+            InstanceIdentifier = instanceIdentifier ?? throw new ArgumentNullException(nameof(instanceIdentifier));
+            _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
+            SetEnabled(_configuration.Enabled);
+            ScanAssemblies();
+        }
+
+        private void ScanAssemblies()
+        {
+            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+            foreach (var assembly in assemblies)
+            {
+                var types = assembly.GetTypesWithAttribute<MonitoredClassAttribute>(true);
+                foreach (var type in types)
+                {
+                    var monItemGeneral = new MonItemGeneral
+                    {
+                        InstanceIdentifier = InstanceIdentifier,
+                        Caption = type.GetCustomAttribute<MonitoredClassAttribute>()?.Caption ?? type.Name,
+                        Description = type.GetCustomAttribute<MonitoredClassAttribute>()?.Description ?? string.Empty,
+                        ViewType = MonItemGeneral.MonViewType.Item
+                    };
+
+                    var properties = type.GetPropertiesWithAttribute<MonitoredPropertyAttribute>(true);
+                    foreach (var property in properties)
+                    {
+                        var itemDescriptor = new MonItemGeneral.ItemDescriptor
+                        {
+                            Name = property.Name,
+                            TypeName = property.PropertyType.Name,
+                            Label = property.GetCustomAttribute<MonitoredPropertyAttribute>()?.Label ?? property.Name,
+                            Description = property.GetCustomAttribute<MonitoredPropertyAttribute>()?.Description ?? string.Empty,
+                            Order = property.GetCustomAttribute<MonitoredPropertyAttribute>()?.Order ?? 0,
+                            _propertyInfo = property
+                        };
+                        monItemGeneral.Items.Add(itemDescriptor);
+                    }
+                    _generalCache.Add(type.FullName!, monItemGeneral);
+                }
+            }
+        }
+
+        #endregion
+
+        #region *** Public Operations ***
+        #endregion
+
+        #region *** Protected Operations ***
+        protected override void OnDisposing()
+        {
+            Close();
+            _generalCache.Clear();
+        }
+
+        private void Open()
+        {
+            if (_udpClientGeneral != null) throw new InvalidOperationException("UDP client channel General already open. Close first.");
+            if (_udpClientData != null) throw new InvalidOperationException("UDP client channel Data already open. Close first.");
+
+            _udpClientGeneral = CreateUdpClient(_configuration.TargetUriGeneral);
+            _udpClientData = CreateUdpClient(_configuration.TargetUriData);
+
+            _timerGeneral = new Timer(OnTimerGeneral, this, _configuration.EmitGeneralInterval, _configuration.EmitGeneralInterval);
+            _timerData = new Timer(OnTimerData, this, _configuration.EmitDataInterval, _configuration.EmitDataInterval);
+        }
+
+        private void OnTimerData(object? state)
+        {
+            throw new NotImplementedException();
+        }
+
+        private void OnTimerGeneral(object? state)
+        {
+            throw new NotImplementedException();
+        }
+
+        private void Close()
+        {
+            if (_udpClientGeneral != null)
+            {
+                _timerGeneral?.Dispose();
+                _udpClientGeneral.Close();
+                _udpClientGeneral.Dispose();
+                _udpClientGeneral = null;
+                _timerGeneral = null;
+            }
+
+            if (_udpClientData != null)
+            {
+                _timerData?.Dispose();
+                _udpClientData.Close();
+                _udpClientData.Dispose();
+                _udpClientData = null;
+                _timerData = null;
+            }
+        }
+
+        private UdpClient CreateUdpClient(Uri targetUri)
+        {
+            var udpClient = new UdpClient(new IPEndPoint(IPAddress.Parse(targetUri.DnsSafeHost), targetUri.Port));
+            return udpClient;
+        }
+        private void SetEnabled(bool value)
+        {  
+            if (value == _isEnabled)
+                return;
+            _isEnabled = value;
+            if (_isEnabled)
+                Open();
+            else
+                Close();
+        }
+        #endregion
+
+        #region *** Nested Types ***
+        #endregion
+    }
+}

+ 24 - 0
Workbench/QMonitor/qmonlib/QMonConfiuration.cs

@@ -0,0 +1,24 @@
+namespace qmonlib
+{
+    public class QMonConfiuration
+    {
+        public bool Enabled { get; set; }
+        public Uri TargetUriGeneral { get; set; }
+        public Uri TargetUriData { get; set; }
+
+        public TimeSpan EmitGeneralInterval { get; set; }
+        public TimeSpan EmitDataInterval { get; set; }
+
+        public static QMonConfiuration CreateDefault()
+        {
+            return new QMonConfiuration
+            {
+                Enabled = true,
+                TargetUriGeneral = new Uri("udp://0.0.0.0:5100"),
+                TargetUriData = new Uri("udp://0.0.0.0:5101"),
+                EmitGeneralInterval = TimeSpan.FromSeconds(5),
+                EmitDataInterval = TimeSpan.FromSeconds(1)
+            };
+        }
+    }
+}

+ 10 - 0
Workbench/QMonitor/qmonlib/qmonlib.csproj

@@ -0,0 +1,10 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+</Project>

+ 25 - 0
Workbench/QMonitor/qmonlib/qmonlib.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.7.34024.191
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qmonlib", "qmonlib.csproj", "{457AA4B2-484E-4CF5-89D7-867759F3E59A}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{457AA4B2-484E-4CF5-89D7-867759F3E59A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{457AA4B2-484E-4CF5-89D7-867759F3E59A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{457AA4B2-484E-4CF5-89D7-867759F3E59A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{457AA4B2-484E-4CF5-89D7-867759F3E59A}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {DB83356C-B824-49D2-9EB0-8A064BE4F199}
+	EndGlobalSection
+EndGlobal