Dalibor Votruba 2 лет назад
Родитель
Сommit
3ff227603f
33 измененных файлов с 1175 добавлено и 219 удалено
  1. 454 0
      Workbench/QMonitor/.gitignore
  2. 6 0
      Workbench/QMonitor/Directory.Build.props
  3. 125 0
      Workbench/QMonitor/qmonlib.emit/Program.cs
  4. 17 0
      Workbench/QMonitor/qmonlib.emit/TestStructures/LoggedUser.cs
  5. 17 0
      Workbench/QMonitor/qmonlib.emit/TestStructures/ProcessItem.cs
  6. 25 0
      Workbench/QMonitor/qmonlib.emit/TestStructures/ServiceStatus.cs
  7. 14 0
      Workbench/QMonitor/qmonlib.emit/qmonlib.emit.csproj
  8. 24 0
      Workbench/QMonitor/qmonlib.ui.Desktop/Program.cs
  9. 18 0
      Workbench/QMonitor/qmonlib.ui.Desktop/app.manifest
  10. 19 0
      Workbench/QMonitor/qmonlib.ui.Desktop/qmonlib.ui.Desktop.csproj
  11. 10 0
      Workbench/QMonitor/qmonlib.ui/App.axaml
  12. 36 0
      Workbench/QMonitor/qmonlib.ui/App.axaml.cs
  13. BIN
      Workbench/QMonitor/qmonlib.ui/Assets/avalonia-logo.ico
  14. 6 0
      Workbench/QMonitor/qmonlib.ui/ViewModels/MainViewModel.cs
  15. 7 0
      Workbench/QMonitor/qmonlib.ui/ViewModels/ViewModelBase.cs
  16. 16 0
      Workbench/QMonitor/qmonlib.ui/Views/MainView.axaml
  17. 11 0
      Workbench/QMonitor/qmonlib.ui/Views/MainView.axaml.cs
  18. 12 0
      Workbench/QMonitor/qmonlib.ui/Views/MainWindow.axaml
  19. 11 0
      Workbench/QMonitor/qmonlib.ui/Views/MainWindow.axaml.cs
  20. 22 0
      Workbench/QMonitor/qmonlib.ui/qmonlib.ui.csproj
  21. 1 0
      Workbench/QMonitor/qmonlib/Attributes/MonitoredClassAttribute.cs
  22. 84 0
      Workbench/QMonitor/qmonlib/Dependencies/Console/ConsoleWriter.cs
  23. 113 0
      Workbench/QMonitor/qmonlib/Dependencies/Console/ProgressBar.cs
  24. 78 0
      Workbench/QMonitor/qmonlib/Dependencies/Console/ProgressConsoleWriter.cs
  25. 25 2
      Workbench/QMonitor/qmonlib/Dependencies/Logging/ConsoleLog.cs
  26. 0 66
      Workbench/QMonitor/qmonlib/Program.cs
  27. 1 1
      Workbench/QMonitor/qmonlib/QMonClient.cs
  28. 3 1
      Workbench/QMonitor/qmonlib/QMonReceiver.cs
  29. 0 17
      Workbench/QMonitor/qmonlib/Test/TestItem.cs
  30. 0 99
      Workbench/QMonitor/qmonlib/processServer.json
  31. 1 10
      Workbench/QMonitor/qmonlib/qmonlib.csproj
  32. 19 1
      Workbench/QMonitor/qmonlib/qmonlib.sln
  33. 0 22
      Workbench/QMonitor/qmonlib/testjson.json

+ 454 - 0
Workbench/QMonitor/.gitignore

@@ -0,0 +1,454 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# Tye
+.tye/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+##
+## Visual studio for Mac
+##
+
+
+# globs
+Makefile.in
+*.userprefs
+*.usertasks
+config.make
+config.status
+aclocal.m4
+install-sh
+autom4te.cache/
+*.tar.gz
+tarballs/
+test-results/
+
+# Mac bundle stuff
+*.dmg
+*.app
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+##
+## Visual Studio Code
+##
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json

+ 6 - 0
Workbench/QMonitor/Directory.Build.props

@@ -0,0 +1,6 @@
+<Project>
+  <PropertyGroup>
+    <Nullable>enable</Nullable>
+    <AvaloniaVersion>11.0.2</AvaloniaVersion>
+  </PropertyGroup>
+</Project>

+ 125 - 0
Workbench/QMonitor/qmonlib.emit/Program.cs

@@ -0,0 +1,125 @@
+using System.Diagnostics;
+using qmonlib.emit.TestStructures;
+using Quadarax.Foundation.Core.Logging;
+
+namespace qmonlib.emit // Note: actual namespace depends on the project name.
+{
+    internal class Program
+    {
+        private static Random _rnd = new Random();
+
+        static void Main(string[] args)
+        {
+            Console.WriteLine("qmonlib.emit * qmon random activity generator");
+            Console.WriteLine("usage: qmonlib.emit <instance_name>");
+            var instanceIdentifier = args.Length > 0 ? args[0] : "test";
+
+            using (var qmonClient = new QMonClient(instanceIdentifier, QMonClientConfiuration.CreateDefault(), new ConsoleLogger()))
+            {
+                var ctx = new Context
+                {
+                    Client = qmonClient,
+                    Status = new ServiceStatus(ServiceStatus.StatusEnum.Online, DateTime.Now),
+                    AvailableUsers = CreateAvailableUsers()
+                };
+                var tmStatus = new Timer(OnServiceStatusTimer, ctx, TimeSpan.Zero, TimeSpan.FromSeconds(5));
+                var tmProcesses = new Timer(OnProcessesTimer, ctx, TimeSpan.Zero, TimeSpan.FromSeconds(2));
+                var tmUsers = new Timer(OnUsersTimer, ctx, TimeSpan.Zero, TimeSpan.FromSeconds(2));
+
+
+                Console.WriteLine("Press any key to stop...");
+                Console.ReadKey();
+                tmStatus.Dispose();
+                tmProcesses.Dispose();
+                tmUsers.Dispose();
+                Console.WriteLine("Stopped.");
+            }
+        }
+
+        private static void OnUsersTimer(object? state)
+        {
+            Console.WriteLine("* OnUsersTimer");
+            if (state == null) return;
+            var ctx = (Context)state;
+
+            foreach (var user in ctx.AvailableUsers)
+            {
+                var randomBool = _rnd.Next(2) == 1;
+                if (randomBool)
+                    ctx.Client.Notify(user);
+            }
+        }
+
+        private static void OnProcessesTimer(object? state)
+        {
+            Console.WriteLine("* OnProcessesTimer");
+            if (state == null) return;
+            var ctx = (Context)state;
+
+            var processes = Process.GetProcesses();
+            foreach (var process in processes)
+            {
+                if (process.Id == 0 || process.Id == 4) continue; // skip system idle process (it's not real process
+
+                var ntfProcess = new ProcessItem()
+                {
+                    Id = process.Id,
+                    Name = process.ProcessName,
+                    Status = process.Responding ? "Running" : "Not Responding",
+                    Path = process.ProcessName + ".exe"
+                };
+                ctx.Client.Notify(ntfProcess);
+            }
+        }
+
+        private static void OnServiceStatusTimer(object? state)
+        {
+            Console.WriteLine("* OnServiceStatusTimer");
+            if (state == null) return;
+            var ctx = (Context)state;
+            var randomBool = _rnd.Next(2) == 1;
+            if (randomBool)
+            {
+                if (ctx.Status.Status == ServiceStatus.StatusEnum.Offline)
+                {
+                    ctx.Status.Status = ServiceStatus.StatusEnum.Online;
+                    ctx.Status.Started = DateTime.Now;
+                }
+            }
+            else
+            {
+                ctx.Status.Status = ServiceStatus.StatusEnum.Offline;
+            }
+            ctx.Client.Notify(ctx.Status);
+        }
+
+        public static LoggedUser[] CreateAvailableUsers()
+        {
+            return new[]
+            {
+                new LoggedUser() { Id = 1, FirstName = "Karel", LastName = "Gott", Login = "karel.gott" },
+                new LoggedUser() { Id = 2, FirstName = "Vasil", LastName = "Bilak", Login = "vasko32" },
+                new LoggedUser() { Id = 3, FirstName = "Alois", LastName = "Šoule", Login = "alois.soule" },
+                new LoggedUser() { Id = 4, FirstName = "Květoslav", LastName = "Pytlík", Login = "kvetak55" },
+                new LoggedUser() { Id = 5, FirstName = "Jindra", LastName = "Kedlub", Login = "kedlubna" },
+                new LoggedUser() { Id = 6, FirstName = "Vlasta", LastName = "Henych", Login = "retard" },
+                new LoggedUser() { Id = 7, FirstName = "Petr", LastName = "Volavka", Login = "petro.vol" },
+                new LoggedUser() { Id = 8, FirstName = "Jan", LastName = "Prášil", Login = "jenda45" },
+                new LoggedUser() { Id = 9, FirstName = "Donald", LastName = "Paprika", Login = "spicy.leco" },
+                new LoggedUser() { Id = 10, FirstName = "Mustafa", LastName = "Novák", Login = "vlastenec48" },
+            };
+        }
+
+        public class Context
+        {
+            public QMonClient Client { get; set; }
+            
+            public ServiceStatus Status { get; set; }
+
+            public LoggedUser[] AvailableUsers { get; set; }
+        }
+
+
+
+    }
+}

+ 17 - 0
Workbench/QMonitor/qmonlib.emit/TestStructures/LoggedUser.cs

@@ -0,0 +1,17 @@
+using qmonlib.Attributes;
+
+namespace qmonlib.emit.TestStructures
+{
+    [MonitoredClass(Caption = "Logged User", Description = "Shows current logged user", ViewType = MonItemGeneral.MonViewType.List, TTL = 5000)]
+    public class LoggedUser
+    {
+        [MonitoredProperty(Label = "Id", Description = "User Id")]
+        public long Id { get; set; }
+        [MonitoredProperty(Label = "Login", Description = "User Login")]
+        public string Login { get; set; }
+        [MonitoredProperty(Label = "First Name", Description = "User First Name")]
+        public string FirstName { get; set; }
+        [MonitoredProperty(Label = "Last Name", Description = "User Last Name")]
+        public string LastName { get; set; }
+    }
+}

+ 17 - 0
Workbench/QMonitor/qmonlib.emit/TestStructures/ProcessItem.cs

@@ -0,0 +1,17 @@
+using qmonlib.Attributes;
+
+namespace qmonlib.emit.TestStructures
+{
+    [MonitoredClass(Caption = "Processes", Description = "Shows current processes status", ViewType = MonItemGeneral.MonViewType.List, TTL = 10000)]
+    public class ProcessItem
+    {
+        [MonitoredProperty(Label = "ProcessId", Description = "Process Id")]
+        public long Id { get; set; }
+        [MonitoredProperty(Label = "ProcessName", Description = "Process Name")]
+        public string Name { get; set; }
+        [MonitoredProperty(Label = "ProcessStatus", Description = "Process Status")]
+        public string Status { get; set; }
+        [MonitoredProperty(Label = "Path", Description = "Process Path")]
+        public string Path { get; set; }
+    }
+}

+ 25 - 0
Workbench/QMonitor/qmonlib.emit/TestStructures/ServiceStatus.cs

@@ -0,0 +1,25 @@
+using qmonlib.Attributes;
+
+namespace qmonlib.emit.TestStructures
+{
+    [MonitoredClass(Caption = "Service Status", Description = "Shows current service status", ViewType = MonItemGeneral.MonViewType.Item)]
+    public class ServiceStatus
+    {
+        [MonitoredProperty(Label = "Status", Description = "Current service status")]
+        public StatusEnum Status { get; set; }
+        [MonitoredProperty(Label = "Started", Description = "Time when service was started")]
+        public DateTime Started { get; set; }
+
+        public ServiceStatus(StatusEnum status, DateTime started)
+        {
+            Status = status;
+            Started = started;
+        }
+
+        public enum StatusEnum
+        {
+            Online,
+            Offline,
+        } 
+    }
+}

+ 14 - 0
Workbench/QMonitor/qmonlib.emit/qmonlib.emit.csproj

@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\qmonlib\qmonlib.csproj" />
+  </ItemGroup>
+
+</Project>

+ 24 - 0
Workbench/QMonitor/qmonlib.ui.Desktop/Program.cs

@@ -0,0 +1,24 @@
+using System;
+
+using Avalonia;
+using Avalonia.ReactiveUI;
+
+namespace qmonlib.ui.Desktop;
+
+class Program
+{
+    // Initialization code. Don't use any Avalonia, third-party APIs or any
+    // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+    // yet and stuff might break.
+    [STAThread]
+    public static void Main(string[] args) => BuildAvaloniaApp()
+        .StartWithClassicDesktopLifetime(args);
+
+    // Avalonia configuration, don't remove; also used by visual designer.
+    public static AppBuilder BuildAvaloniaApp()
+        => AppBuilder.Configure<App>()
+            .UsePlatformDetect()
+            .WithInterFont()
+            .LogToTrace()
+            .UseReactiveUI();
+}

+ 18 - 0
Workbench/QMonitor/qmonlib.ui.Desktop/app.manifest

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <!-- This manifest is used on Windows only.
+       Don't remove it as it might cause problems with window transparency and embeded controls.
+       For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
+  <assemblyIdentity version="1.0.0.0" name="AvaloniaTest.Desktop"/>
+
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- A list of the Windows versions that this application has been tested on
+           and is designed to work with. Uncomment the appropriate elements
+           and Windows will automatically select the most compatible environment. -->
+
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+    </application>
+  </compatibility>
+</assembly>

+ 19 - 0
Workbench/QMonitor/qmonlib.ui.Desktop/qmonlib.ui.Desktop.csproj

@@ -0,0 +1,19 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <!--If you are willing to use Windows/MacOS native APIs you will need to create 3 projects.
+    One for Windows with net7.0-windows TFM, one for MacOS with net7.0-macos and one with net7.0 TFM for Linux.-->
+    <TargetFramework>net7.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Avalonia.Desktop" Version="$(AvaloniaVersion)" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\qmonlib.ui\qmonlib.ui.csproj" />
+  </ItemGroup>
+</Project>

+ 10 - 0
Workbench/QMonitor/qmonlib.ui/App.axaml

@@ -0,0 +1,10 @@
+<Application xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             x:Class="qmonlib.ui.App"
+             RequestedThemeVariant="Default">
+             <!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
+
+    <Application.Styles>
+        <FluentTheme />
+    </Application.Styles>
+</Application>

+ 36 - 0
Workbench/QMonitor/qmonlib.ui/App.axaml.cs

@@ -0,0 +1,36 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+
+using qmonlib.ui.ViewModels;
+using qmonlib.ui.Views;
+
+namespace qmonlib.ui;
+
+public partial class App : Application
+{
+    public override void Initialize()
+    {
+        AvaloniaXamlLoader.Load(this);
+    }
+
+    public override void OnFrameworkInitializationCompleted()
+    {
+        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+        {
+            desktop.MainWindow = new MainWindow
+            {
+                DataContext = new MainViewModel()
+            };
+        }
+        else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
+        {
+            singleViewPlatform.MainView = new MainView
+            {
+                DataContext = new MainViewModel()
+            };
+        }
+
+        base.OnFrameworkInitializationCompleted();
+    }
+}

BIN
Workbench/QMonitor/qmonlib.ui/Assets/avalonia-logo.ico


+ 6 - 0
Workbench/QMonitor/qmonlib.ui/ViewModels/MainViewModel.cs

@@ -0,0 +1,6 @@
+namespace qmonlib.ui.ViewModels;
+
+public class MainViewModel : ViewModelBase
+{
+    public string Greeting => "Welcome to Avalonia!";
+}

+ 7 - 0
Workbench/QMonitor/qmonlib.ui/ViewModels/ViewModelBase.cs

@@ -0,0 +1,7 @@
+using ReactiveUI;
+
+namespace qmonlib.ui.ViewModels;
+
+public class ViewModelBase : ReactiveObject
+{
+}

+ 16 - 0
Workbench/QMonitor/qmonlib.ui/Views/MainView.axaml

@@ -0,0 +1,16 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:vm="clr-namespace:qmonlib.ui.ViewModels"
+             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+             x:Class="qmonlib.ui.Views.MainView"
+             x:DataType="vm:MainViewModel">
+  <Design.DataContext>
+    <!-- This only sets the DataContext for the previewer in an IDE,
+         to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
+    <vm:MainViewModel />
+  </Design.DataContext>
+
+  <TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
+</UserControl>

+ 11 - 0
Workbench/QMonitor/qmonlib.ui/Views/MainView.axaml.cs

@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace qmonlib.ui.Views;
+
+public partial class MainView : UserControl
+{
+    public MainView()
+    {
+        InitializeComponent();
+    }
+}

+ 12 - 0
Workbench/QMonitor/qmonlib.ui/Views/MainWindow.axaml

@@ -0,0 +1,12 @@
+<Window xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:vm="using:qmonlib.ui.ViewModels"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:views="clr-namespace:qmonlib.ui.Views"
+        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+        x:Class="qmonlib.ui.Views.MainWindow"
+        Icon="/Assets/avalonia-logo.ico"
+        Title="qmonlib.ui">
+        <views:MainView />
+</Window>

+ 11 - 0
Workbench/QMonitor/qmonlib.ui/Views/MainWindow.axaml.cs

@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace qmonlib.ui.Views;
+
+public partial class MainWindow : Window
+{
+    public MainWindow()
+    {
+        InitializeComponent();
+    }
+}

+ 22 - 0
Workbench/QMonitor/qmonlib.ui/qmonlib.ui.csproj

@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net7.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <LangVersion>latest</LangVersion>
+    <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
+  </PropertyGroup>
+
+  
+  <ItemGroup>
+    <AvaloniaResource Include="Assets\**" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
+    <PackageReference Include="Avalonia.Themes.Fluent" Version="$(AvaloniaVersion)" />
+    <PackageReference Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)" />
+    <PackageReference Include="Avalonia.ReactiveUI" Version="$(AvaloniaVersion)" />
+    <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
+    <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)" />
+  </ItemGroup>
+</Project>

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

@@ -5,6 +5,7 @@
     {
         public string Caption { get; set; }
         public string Description { get; set; }
+        public long TTL { get; set; }
         public MonItemGeneral.MonViewType ViewType { get; set; }
     }
 }

+ 84 - 0
Workbench/QMonitor/qmonlib/Dependencies/Console/ConsoleWriter.cs

@@ -0,0 +1,84 @@
+using System;
+using Quadarax.Foundation.Core.Object;
+
+namespace Quadarax.Foundation.Core.Console
+{
+    public class ConsoleWriter : DisposableObject
+    {
+        private readonly ConsoleColor _oldBackColor;
+        private readonly ConsoleColor _oldForeColor;
+        private readonly ConsoleColor _backColor;
+        private readonly ConsoleColor _foreColor;
+
+
+        public ConsoleWriter(ConsoleColor foreColor,ConsoleColor backColor)
+        {
+            _oldBackColor = System.Console.BackgroundColor;
+            _oldForeColor = System.Console.ForegroundColor;
+            _backColor = backColor;
+            _foreColor = foreColor;
+        }
+
+        public ConsoleWriter(ConsoleColor foreColor)
+        {
+            _oldBackColor = System.Console.BackgroundColor;
+            _oldForeColor = System.Console.ForegroundColor;
+            _backColor = _oldBackColor;
+            _foreColor = foreColor;
+        }
+
+
+        public ConsoleWriter WriteLine(string text)
+        {
+            SetColor();
+            WriteInternal(text);
+            WriteLineInternal();
+            return this;
+        }
+
+        public string ReadLine()
+        {
+            SetColor();
+            return System.Console.ReadLine();
+        }
+
+        public ConsoleWriter WriteLine()
+        {
+            WriteLineInternal();
+            return this;
+        }
+        public ConsoleWriter Write(string text)
+        {
+            SetColor();
+            WriteInternal(text);
+            return this;
+        }
+
+        public static void Clear()
+        {
+            System.Console.Clear();
+        }
+
+        protected virtual void WriteInternal(string text)
+        {
+            System.Console.Write(text);
+        }
+        protected virtual void WriteLineInternal()
+        {
+            System.Console.WriteLine();
+        }
+
+        protected override void OnDisposing()
+        {
+            System.Console.ForegroundColor = _oldForeColor;
+            System.Console.BackgroundColor = _oldBackColor;
+        }
+
+        private void SetColor()
+        {
+            System.Console.ForegroundColor = _foreColor;
+            System.Console.BackgroundColor = _backColor;
+        }
+
+    }
+}

+ 113 - 0
Workbench/QMonitor/qmonlib/Dependencies/Console/ProgressBar.cs

@@ -0,0 +1,113 @@
+using System;
+using System.Text;
+using System.Threading;
+using Quadarax.Foundation.Core.Object;
+// ReSharper disable InconsistentNaming
+
+namespace Quadarax.Foundation.Core.Console
+{
+    public class ProgressBar : DisposableObject, IProgress<double>
+    {
+
+        #region *** Constants ***
+        private const int CN_BLOCK_COUNT = 10;
+        private const string CS_ANIMATION = @"|/-\";
+        #endregion
+        #region *** Private fields ***
+        private readonly TimeSpan _animationInterval = TimeSpan.FromSeconds(1.0 / 8);
+        private readonly Timer _timer;
+        private double _currentProgress;
+        private string _currentText = string.Empty;
+        private int _animationIndex;
+        private ConsoleWriter _writer;
+        #endregion
+
+        #region *** Constructors ***
+        public ProgressBar(ConsoleWriter writer)
+        {
+            _writer = writer ?? throw new ArgumentNullException(nameof(writer));
+            _timer = new Timer(TimerHandler);
+
+            // A progress bar is only for temporary display in a console window.
+            // If the console output is redirected to a file, draw nothing.
+            // Otherwise, we'll end up with a lot of garbage in the target file.
+            if (!System.Console.IsOutputRedirected)
+            {
+                ResetTimer();
+            }
+        }
+        #endregion
+
+        #region *** Public Operations ***
+        /// <summary>
+        /// Update progress status.
+        /// </summary>
+        /// <param name="value">Status value between 0 - 1. Represents percentage.</param>
+        public void Report(double value)
+        {
+            // Make sure value is in [0..1] range
+            value = Math.Max(0, Math.Min(1, value));
+            Interlocked.Exchange(ref _currentProgress, value);
+        }
+        #endregion
+
+        #region *** Private Operations ***
+        private void TimerHandler(object state)
+        {
+            lock (_timer)
+            {
+                var progressBlockCount = (int)(_currentProgress * CN_BLOCK_COUNT);
+                var percent = (int)(_currentProgress * 100);
+                var text = $"[{new string('#', progressBlockCount)}{new string('-', CN_BLOCK_COUNT - progressBlockCount)}] {percent,3}% {CS_ANIMATION[_animationIndex++ % CS_ANIMATION.Length]}";
+                UpdateText(text);
+                ResetTimer();
+            }
+        }
+
+        private void UpdateText(string text)
+        {
+            // Get length of common portion
+            var commonPrefixLength = 0;
+            var commonLength = Math.Min(_currentText.Length, text.Length);
+            while (commonPrefixLength < commonLength && text[commonPrefixLength] == _currentText[commonPrefixLength])
+            {
+                commonPrefixLength++;
+            }
+
+            // Backtrack to the first differing character
+            var outputBuilder = new StringBuilder();
+            outputBuilder.Append('\b', _currentText.Length - commonPrefixLength);
+
+            // Output new suffix
+            outputBuilder.Append(text.Substring(commonPrefixLength));
+
+            // If the new text is shorter than the old one: delete overlapping characters
+            var overlapCount = _currentText.Length - text.Length;
+            if (overlapCount > 0)
+            {
+                outputBuilder.Append(' ', overlapCount);
+                outputBuilder.Append('\b', overlapCount);
+            }
+
+            _writer.Write(outputBuilder.ToString());
+            _currentText = text;
+        }
+
+        private void ResetTimer()
+        {
+            _timer.Change(_animationInterval, TimeSpan.FromMilliseconds(-1));
+        }
+        #endregion
+
+        #region *** Private Overrides ***
+        protected override void OnDisposing()
+        {
+            lock (_timer)
+            {
+                UpdateText(string.Empty);
+                _writer = null;
+            }
+        }
+        #endregion
+    }
+}

+ 78 - 0
Workbench/QMonitor/qmonlib/Dependencies/Console/ProgressConsoleWriter.cs

@@ -0,0 +1,78 @@
+using System;
+using System.Text;
+
+namespace Quadarax.Foundation.Core.Console
+{
+    public class ProgressConsoleWriter : ConsoleWriter
+    {
+        #region *** Private Fields ***
+        private int _maxProgress;
+        private int _curProgress = 0;
+        private string _lastText;
+        #endregion
+
+        #region *** Constructors ***
+        public ProgressConsoleWriter(ConsoleColor foreColor, ConsoleColor backColor, int maxProgress) : base(foreColor, backColor)
+        {
+            _maxProgress = maxProgress;
+        }
+
+        public ProgressConsoleWriter(ConsoleColor foreColor, int maxProgress) : base(foreColor)
+        {
+            _maxProgress = maxProgress;
+        }
+        #endregion
+
+        #region *** Public Operations ***
+
+        public void SetProgress(int progress)
+        {
+            _curProgress = progress;
+            OnRefreshProgress();
+        }
+        public void IncProgress()
+        {
+            _curProgress++;
+            OnRefreshProgress();
+        }
+        public void DecProgress()
+        {
+            _curProgress--;
+            OnRefreshProgress();
+        }
+        
+        #endregion
+
+        #region *** Internal Operations ***
+
+        protected override void WriteLineInternal()
+        {
+            base.WriteLineInternal();
+            OnRefreshProgress();
+        }
+
+        protected override void WriteInternal(string text)
+        {
+            _lastText = text;
+            OnRefreshProgress();
+        }
+
+        private void OnRefreshProgress()
+        {
+            base.WriteInternal("\r");
+            base.WriteInternal(new string(' ', System.Console.BufferWidth - 1));
+            base.WriteInternal("\r");
+            var sb = new StringBuilder();
+            sb.Append("[").Append(_curProgress).Append("/").Append(_maxProgress).Append("] ").Append(_lastText);
+            base.WriteInternal(sb.ToString());
+        }
+        protected override void OnDisposing()
+        {
+            System.Console.WriteLine();
+            base.OnDisposing();
+        }
+
+        #endregion
+
+    }
+}

+ 25 - 2
Workbench/QMonitor/qmonlib/Dependencies/Logging/ConsoleLog.cs

@@ -1,9 +1,16 @@
-namespace Quadarax.Foundation.Core.Logging
+using Quadarax.Foundation.Core.Console;
+
+namespace Quadarax.Foundation.Core.Logging
 {
     public class ConsoleLog : ILog
     {
         private string _typeName;
 
+		private ConsoleWriter _infoWriter = new ConsoleWriter(ConsoleColor.White);
+        private ConsoleWriter _debugWriter = new ConsoleWriter(ConsoleColor.Green);
+        private ConsoleWriter _warnWriter = new ConsoleWriter(ConsoleColor.Yellow);
+        private ConsoleWriter _errorWriter = new ConsoleWriter(ConsoleColor.Red);
+        private ConsoleWriter _traceWriter = new ConsoleWriter(ConsoleColor.DarkGray);
         public ConsoleLog(Type owningType)
         {
             _typeName = owningType.Name;
@@ -25,7 +32,23 @@
             var thNum = Thread.CurrentThread.ManagedThreadId;
 
             var final = $"[{severity}:{now.ToLongTimeString()}:{thNum}]#{_typeName}: {message}";
-            Console.WriteLine(final);
+
+            switch (severity)
+            {
+                case LogSeverityEnum.Info:
+                    _infoWriter.WriteLine(final);
+                    break;
+                case LogSeverityEnum.Debug:
+                    _debugWriter.WriteLine(final);
+                    break;
+                case LogSeverityEnum.Warn:
+                    _warnWriter.WriteLine(final);
+                    break;
+                case LogSeverityEnum.Fatal:
+                case LogSeverityEnum.Error:
+                    _errorWriter.WriteLine(final);
+                    break;
+            }
         }
 
         public void Log(LogSeverityEnum severity, string message, Exception? exception)

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

@@ -1,66 +0,0 @@
-// See https://aka.ms/new-console-template for more information
-
-
-using System.IO.Abstractions;
-using qmonlib;
-using qmonlib.Test;
-using Quadarax.Foundation.Core.Logging;
-
-var logger = new ConsoleLogger();
-
-var cancel = new CancellationTokenSource();
-
-new Thread(() =>
-{
-    OnStartReceiver(cancel.Token);
-
-}).Start();
-Console.WriteLine("Waiting for receiver starts...");
-Thread.Sleep(5000);
-using (var cli = new QMonClient("TST", QMonClientConfiuration.CreateDefault(), logger))
-{
-
-    cli.Notify(new TestItem()
-    {
-        Age = 4,
-        FirstName = "Adam",
-        LastName = "Votruba"
-    });
-    cli.Notify(new TestItem()
-    {
-        Age = 47,
-        FirstName = "Dalibor",
-        LastName = "Votruba"
-    });
-    cli.Notify(new TestItem()
-    {
-        Age = 43,
-        FirstName = "Dita",
-        LastName = "Rychetská"
-    });
-
-
-    Console.WriteLine("Press any key to close...");
-    Console.ReadKey();
-    cancel.Cancel();
-}
-
-void OnGeneralReceived(MonGeneral general)
-{
-    Console.WriteLine($"General received: {general.InstanceIdentifier} items count {general.Items.Count}");
-}
-
-void OnDataReceived(MonData[] data)
-{
-    Console.WriteLine($"Data received: Count={data.Length}");
-}
-
-void OnStartReceiver(CancellationToken cancelToken)
-{
-    using (var rcv = new QMonReceiver( new FileSystem(), QMonReceiverConfiguration.CreateDefault(), logger, OnGeneralReceived, OnDataReceived))
-    {
-        while (!cancelToken.IsCancellationRequested)
-            Thread.Sleep(100);
-    }
-    Console.WriteLine("Receiver stopped");
-}

+ 1 - 1
Workbench/QMonitor/qmonlib/QMonClient.cs

@@ -124,7 +124,7 @@ namespace qmonlib
                 {
                     var dataOut = Serialize(data);
                     declaredSize += dataOut.Length;
-                    sentSize += owner.UdpClientGeneral.Send(dataOut);
+                    sentSize += owner.UdpClientData.Send(dataOut);
                     _dataCache.Remove(data);
                     itmCnt++;
                 }

+ 3 - 1
Workbench/QMonitor/qmonlib/QMonReceiver.cs

@@ -96,7 +96,7 @@ namespace qmonlib
             foreach (var instance in instances)
             {
                 var dirName = _fileSystem.Path.Combine(Configuration.DataCache.Path, instance);
-                if (_fileSystem.Directory.Exists(dirName))
+                if (!_fileSystem.Directory.Exists(dirName))
                 {
                     _fileSystem.Directory.CreateDirectory(dirName);
                     Log(LogSeverityEnum.Debug,$"Data receiver created data cache directory '{dirName}'");
@@ -156,11 +156,13 @@ namespace qmonlib
             if (cancellation.IsCancellationRequested)
             {
                 Log(LogSeverityEnum.Debug, $"[ReceiverData] Cancellation token detected on listener '{UriData}'.");
+                return;
             }
             try
             {
                 if (UdpClientData.Available==0) return;
                 var remoteEndPoint = new IPEndPoint(IPAddress.Parse(this.UriData.DnsSafeHost), this.UriData.Port);
+                Log(LogSeverityEnum.Debug, $"[ReceiverData] Waiting for data on '{UriData}'...");
                 var data = UdpClientData.Receive(ref remoteEndPoint);
                 var monData = JsonSerializer.Deserialize<MonData>(data, _jsonSerializerOptions);
                 if (monData == null)

+ 0 - 17
Workbench/QMonitor/qmonlib/Test/TestItem.cs

@@ -1,17 +0,0 @@
-using qmonlib.Attributes;
-
-namespace qmonlib.Test
-{
-    [MonitoredClass(Caption = "Person", Description = "Simple personal info", ViewType = MonItemGeneral.MonViewType.List)]
-    public class TestItem
-    {
-        [MonitoredPropertyKey(KeyOrder = 1)]
-        [MonitoredProperty(Name = "FirstName", Label="First Name", Description = "First name of the person", Order = 1)]
-        public string FirstName { get; set; }
-        [MonitoredPropertyKey(KeyOrder = 0)]
-        [MonitoredProperty(Name = "LastName", Label="Last Name", Description = "Last name of the person", Order = 0)]
-        public string LastName { get; set; }
-        [MonitoredProperty(Name = "Age", Description = "Age of the person", Order = 2)]
-        public int Age { get; set;}
-    }
-}

+ 0 - 99
Workbench/QMonitor/qmonlib/processServer.json

@@ -1,99 +0,0 @@
-{
-	"api-base-url": "http://localhost:5000/api/",
-	"api-user": "Process",
-	"api-pwd": "SmRAd1avKA",
-	"api-magic": "SHARE",
-	"api-timeout": "00:10:00",
-	"System": {
-		"mode": "Proxy",
-		"identification": "DevNode#1",
-		"preferred-interface": "auto",
-		"pool-pull-documents": 4,
-		"pool-pull-interval": "00:01:00",
-		"pool-path-base": "D:\\BO.PS",
-		"pool-path-pending": ".pending",
-		"pool-path-working": ".working",
-		"pool-path-done": ".done",
-		"pool-path-fail": ".fail",
-		"pool-retention": {
-			"enabled": true,
-			"retention-": "24:00:00",
-			"size": 1000000000,
-			"items": 100
-		},
-		"status-monitoring": {
-			"enabled": true,
-			"file": "{CD}\\status.csv",
-			"monitoring-interval": "00:00:10"
-		},
-
-		"scenario-path-base": "{CD}\\Scenarios",
-    "scenario-parallels": 2,
-		"Scenarios": [
-			{
-				"name": "test",
-				"accept-filters": [
-					{
-						"code": "profile",
-						"value": "test"
-					}
-				],
-				"cmd": "scenario-dummy.cmd",
-				"args": "{filenames} {billingplan}",
-				"timeout-interval": "01:00:00",
-				"exitcode-succ": 0,
-				"exitcode-fail": 1
-			}
-		],
-		"Proxy" : {
-			"Process": {
-				"Succ": [
-					true,
-					false
-				],
-        "Output": [
-          "result.txt"
-        ]
-			},
-			"App": {
-				"default-documents-count": 4,
-        "max-documents-count": 20,
-				"default-artifacts-count": 2,
-				"Extension": [
-					".cad",
-					".txt",
-					".catia"
-				],
-				"NamePrefix": [
-					"object",
-					"wing",
-					"holo"
-				],
-				"PresetCreditCost": [
-					1,
-					2,
-					3
-				],
-				"PresetWatermark": [
-					true,
-					false
-				],
-				"PresetQuality": [
-					0,
-					1,
-					2,
-					3
-				],
-				"PresetProcessProfile": [
-					"TEST",
-					"HIRES",
-					"LOWRES"
-				],
-				"PresetTest": [
-					true,
-					false
-				]
-			} 
-    } 
-	}
-}

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

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <OutputType>Exe</OutputType>
+    <OutputType>Library</OutputType>
     <TargetFramework>net6.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
@@ -11,13 +11,4 @@
     <PackageReference Include="System.IO.Abstractions" Version="19.2.69" />
   </ItemGroup>
 
-  <ItemGroup>
-    <None Update="processServer.json">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-    </None>
-    <None Update="testjson.json">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-    </None>
-  </ItemGroup>
-
 </Project>

+ 19 - 1
Workbench/QMonitor/qmonlib/qmonlib.sln

@@ -3,7 +3,13 @@ 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}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qmonlib", "qmonlib.csproj", "{457AA4B2-484E-4CF5-89D7-867759F3E59A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qmonlib.emit", "..\qmonlib.emit\qmonlib.emit.csproj", "{EF539451-1FF9-43FB-B8E2-177484AA2DB0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qmonlib.ui", "..\qmonlib.ui\qmonlib.ui.csproj", "{92499B79-D260-42FC-934D-36DEAEFA3EA8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qmonlib.ui.Desktop", "..\qmonlib.ui.Desktop\qmonlib.ui.Desktop.csproj", "{67EDD759-CD0B-482F-93BD-98E9C33E220F}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +21,18 @@ Global
 		{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
+		{EF539451-1FF9-43FB-B8E2-177484AA2DB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EF539451-1FF9-43FB-B8E2-177484AA2DB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EF539451-1FF9-43FB-B8E2-177484AA2DB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{EF539451-1FF9-43FB-B8E2-177484AA2DB0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{92499B79-D260-42FC-934D-36DEAEFA3EA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{92499B79-D260-42FC-934D-36DEAEFA3EA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{92499B79-D260-42FC-934D-36DEAEFA3EA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{92499B79-D260-42FC-934D-36DEAEFA3EA8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{67EDD759-CD0B-482F-93BD-98E9C33E220F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{67EDD759-CD0B-482F-93BD-98E9C33E220F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{67EDD759-CD0B-482F-93BD-98E9C33E220F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{67EDD759-CD0B-482F-93BD-98E9C33E220F}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 0 - 22
Workbench/QMonitor/qmonlib/testjson.json

@@ -1,22 +0,0 @@
-{
-  "atr1": "val1",
-  "atrbool": true,
-  "colstr": [
-    "colstrval1",
-    "colstrval2"
-  ],
-  "colbool": [
-    true,
-    false
-  ],
-  "colobj": [
-    {
-      "obja1": "obj",
-      "abja2": true
-    },
-    {
-      "obja1": "obj2",
-      "abja2": false
-    }
-  ]
-}