mirror of
https://github.com/9ParsonsB/Pulsar.git
synced 2025-04-05 01:29:38 -04:00
Add project files.
This commit is contained in:
parent
7099cf23c6
commit
a5154996ee
365
.gitignore
vendored
Normal file
365
.gitignore
vendored
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
## 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/
|
||||||
|
[Oo]ut/
|
||||||
|
[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/
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
*.snk
|
25
ObservatoryCore.sln
Normal file
25
ObservatoryCore.sln
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.30128.74
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ObservatoryCore", "ObservatoryCore\ObservatoryCore.csproj", "{0E1C4F16-858E-4E53-948A-77D81A8F3395}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0E1C4F16-858E-4E53-948A-77D81A8F3395}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {F41B8681-A5D9-4167-9938-56DE88024000}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
97
ObservatoryCore/JournalReader.cs
Normal file
97
ObservatoryCore/JournalReader.cs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
using Observatory.Framework.Files.Journal;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Observatory
|
||||||
|
{
|
||||||
|
public class JournalReader
|
||||||
|
{
|
||||||
|
public static TJournal ObservatoryDeserializer<TJournal>(string json) where TJournal : JournalBase
|
||||||
|
{
|
||||||
|
TJournal deserialized;
|
||||||
|
|
||||||
|
if (typeof(TJournal) == typeof(InvalidJson))
|
||||||
|
{
|
||||||
|
InvalidJson invalidJson;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json));
|
||||||
|
string eventType = string.Empty;
|
||||||
|
string timestamp = string.Empty;
|
||||||
|
|
||||||
|
while ((eventType == string.Empty || timestamp == string.Empty) && reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonTokenType.PropertyName)
|
||||||
|
{
|
||||||
|
if (reader.GetString() == "event")
|
||||||
|
{
|
||||||
|
reader.Read();
|
||||||
|
eventType = reader.GetString();
|
||||||
|
}
|
||||||
|
else if (reader.GetString() == "timestamp")
|
||||||
|
{
|
||||||
|
reader.Read();
|
||||||
|
timestamp = reader.GetString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidJson = new InvalidJson()
|
||||||
|
{
|
||||||
|
Event = "InvalidJson",
|
||||||
|
Timestamp = timestamp,
|
||||||
|
OriginalEvent = eventType
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
invalidJson = new InvalidJson()
|
||||||
|
{
|
||||||
|
Event = "InvalidJson",
|
||||||
|
Timestamp = string.Empty,
|
||||||
|
OriginalEvent = "Invalid"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialized = (TJournal)Convert.ChangeType(invalidJson, typeof(TJournal));
|
||||||
|
|
||||||
|
}
|
||||||
|
//Journal potentially had invalid JSON for a brief period in 2017, check for it and remove.
|
||||||
|
//TODO: Check if this gets handled by InvalidJson now.
|
||||||
|
else if (typeof(TJournal) == typeof(Scan) && json.Contains("\"RotationPeriod\":inf"))
|
||||||
|
{
|
||||||
|
deserialized = JsonSerializer.Deserialize<TJournal>(json.Replace("\"RotationPeriod\":inf,", ""));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deserialized = JsonSerializer.Deserialize<TJournal>(json);
|
||||||
|
}
|
||||||
|
deserialized.Json = json;
|
||||||
|
|
||||||
|
return deserialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Dictionary<string, Type> PopulateEventClasses()
|
||||||
|
{
|
||||||
|
var eventClasses = new Dictionary<string, Type>(StringComparer.InvariantCultureIgnoreCase);
|
||||||
|
|
||||||
|
var allTypes = Assembly.GetAssembly(typeof(JournalBase)).GetTypes();
|
||||||
|
|
||||||
|
var journalTypes = allTypes.Where(a => a.IsSubclassOf(typeof(JournalBase)));
|
||||||
|
|
||||||
|
foreach (var journalType in journalTypes)
|
||||||
|
{
|
||||||
|
eventClasses.Add(journalType.Name, journalType);
|
||||||
|
}
|
||||||
|
|
||||||
|
eventClasses.Add("JournalBase", typeof(JournalBase));
|
||||||
|
|
||||||
|
return eventClasses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
265
ObservatoryCore/LogMonitor.cs
Normal file
265
ObservatoryCore/LogMonitor.cs
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Observatory.Framework;
|
||||||
|
using Observatory.Framework.Files;
|
||||||
|
|
||||||
|
namespace Observatory
|
||||||
|
{
|
||||||
|
class LogMonitor
|
||||||
|
{
|
||||||
|
#region Singleton Instantiation
|
||||||
|
|
||||||
|
public static LogMonitor GetInstance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _instance.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Lazy<LogMonitor> _instance = new Lazy<LogMonitor>(NewLogMonitor);
|
||||||
|
|
||||||
|
private static LogMonitor NewLogMonitor()
|
||||||
|
{
|
||||||
|
return new LogMonitor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LogMonitor()
|
||||||
|
{
|
||||||
|
currentLine = new();
|
||||||
|
journalTypes = JournalReader.PopulateEventClasses();
|
||||||
|
InitializeWatchers(string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
journalWatcher.EnableRaisingEvents = true;
|
||||||
|
statusWatcher.EnableRaisingEvents = true;
|
||||||
|
monitoring = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
journalWatcher.EnableRaisingEvents = false;
|
||||||
|
statusWatcher.EnableRaisingEvents = false;
|
||||||
|
monitoring = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ChangeWatchedDirectory(string path)
|
||||||
|
{
|
||||||
|
journalWatcher.Dispose();
|
||||||
|
statusWatcher.Dispose();
|
||||||
|
InitializeWatchers(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMonitoring()
|
||||||
|
{
|
||||||
|
return monitoring;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ReadAllInProgress()
|
||||||
|
{
|
||||||
|
return readall;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReadAllJournals()
|
||||||
|
{
|
||||||
|
ReadAllJournals(string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReadAllJournals(string path)
|
||||||
|
{
|
||||||
|
readall = true;
|
||||||
|
DirectoryInfo logDirectory = GetJournalFolder(path);
|
||||||
|
var files = logDirectory.GetFiles("Journal.????????????.??.log");
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
var lines = ReadAllLines(file.FullName);
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
DeserializeAndInvoke(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readall = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Events
|
||||||
|
|
||||||
|
public event EventHandler<JournalEventArgs> JournalEntry;
|
||||||
|
|
||||||
|
public event EventHandler<JournalEventArgs> StatusUpdate;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region Private Fields
|
||||||
|
|
||||||
|
private FileSystemWatcher journalWatcher;
|
||||||
|
private FileSystemWatcher statusWatcher;
|
||||||
|
private Dictionary<string, Type> journalTypes;
|
||||||
|
private Dictionary<string, int> currentLine;
|
||||||
|
private bool monitoring = false;
|
||||||
|
private bool readall = false;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Methods
|
||||||
|
|
||||||
|
private void InitializeWatchers(string path)
|
||||||
|
{
|
||||||
|
DirectoryInfo logDirectory = GetJournalFolder(path);
|
||||||
|
|
||||||
|
journalWatcher = new FileSystemWatcher(logDirectory.FullName, "Journal.????????????.??.log")
|
||||||
|
{
|
||||||
|
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.LastAccess |
|
||||||
|
NotifyFilters.FileName | NotifyFilters.CreationTime | NotifyFilters.DirectoryName
|
||||||
|
};
|
||||||
|
journalWatcher.Changed += LogChangedEvent;
|
||||||
|
journalWatcher.Created += LogCreatedEvent;
|
||||||
|
|
||||||
|
statusWatcher = new FileSystemWatcher(logDirectory.FullName, "Status.json")
|
||||||
|
{
|
||||||
|
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.LastAccess
|
||||||
|
};
|
||||||
|
statusWatcher.Changed += StatusUpdateEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DirectoryInfo GetJournalFolder(string path)
|
||||||
|
{
|
||||||
|
DirectoryInfo logDirectory;
|
||||||
|
|
||||||
|
if (path.Length == 0 && Properties.Core.Default.JournalFolder.Trim().Length > 0)
|
||||||
|
{
|
||||||
|
path = Properties.Core.Default.JournalFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.Length > 0)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(path))
|
||||||
|
{
|
||||||
|
logDirectory = new DirectoryInfo(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new DirectoryNotFoundException($"Directory '{path}' does not exist.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
logDirectory = new DirectoryInfo(GetSavedGamesPath() + @"\Frontier Developments\Elite Dangerous");
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
{
|
||||||
|
logDirectory = new DirectoryInfo(".");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("Current OS Platform Not Supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Properties.Core.Default.JournalFolder = path;
|
||||||
|
Properties.Core.Default.Save();
|
||||||
|
|
||||||
|
return logDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeserializeAndInvoke(string line)
|
||||||
|
{
|
||||||
|
var eventType = JournalUtilities.GetEventType(line);
|
||||||
|
if (!journalTypes.ContainsKey(eventType))
|
||||||
|
{
|
||||||
|
eventType = "JournalBase";
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventClass = journalTypes[eventType];
|
||||||
|
MethodInfo journalRead = typeof(JournalReader).GetMethod(nameof(JournalReader.ObservatoryDeserializer));
|
||||||
|
MethodInfo journalGeneric = journalRead.MakeGenericMethod(eventClass);
|
||||||
|
object entry = journalGeneric.Invoke(null, new object[] { line });
|
||||||
|
var journalEvent = new JournalEventArgs() { journalType = eventClass, journalEvent = entry };
|
||||||
|
var handler = JournalEntry;
|
||||||
|
|
||||||
|
handler?.Invoke(this, journalEvent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogChangedEvent(object source, FileSystemEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
var fileContent = ReadAllLines(eventArgs.FullPath);
|
||||||
|
|
||||||
|
if (currentLine[eventArgs.FullPath] == -1)
|
||||||
|
{
|
||||||
|
currentLine[eventArgs.FullPath] = fileContent.Count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(string line in fileContent.Skip(currentLine[eventArgs.FullPath]))
|
||||||
|
{
|
||||||
|
DeserializeAndInvoke(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentLine[eventArgs.FullPath] = fileContent.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> ReadAllLines(string path)
|
||||||
|
{
|
||||||
|
var lines = new List<string>();
|
||||||
|
using (StreamReader file = new StreamReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||||
|
{
|
||||||
|
while (!file.EndOfStream)
|
||||||
|
{
|
||||||
|
lines.Add(file.ReadLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogCreatedEvent(object source, FileSystemEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
currentLine[eventArgs.FullPath] = 0;
|
||||||
|
LogChangedEvent(source, eventArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StatusUpdateEvent(object source, FileSystemEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
var handler = StatusUpdate;
|
||||||
|
var statusLines = ReadAllLines(eventArgs.FullPath);
|
||||||
|
if (statusLines.Count > 0)
|
||||||
|
{
|
||||||
|
var status = JournalReader.ObservatoryDeserializer<Status>(statusLines[0]);
|
||||||
|
handler?.Invoke(this, new JournalEventArgs() { journalType = typeof(Status), journalEvent = status });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetSavedGamesPath()
|
||||||
|
{
|
||||||
|
if (Environment.OSVersion.Version.Major < 6) throw new NotSupportedException();
|
||||||
|
IntPtr pathPtr = IntPtr.Zero;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Guid FolderSavedGames = new Guid("4C5C32FF-BB9D-43b0-B5B4-2D72E54EAAA4");
|
||||||
|
SHGetKnownFolderPath(ref FolderSavedGames, 0, IntPtr.Zero, out pathPtr);
|
||||||
|
return Marshal.PtrToStringUni(pathPtr);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Marshal.FreeCoTaskMem(pathPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
|
||||||
|
private static extern int SHGetKnownFolderPath(ref Guid id, int flags, IntPtr token, out IntPtr path);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
23
ObservatoryCore/ObservatoryCore.cs
Normal file
23
ObservatoryCore/ObservatoryCore.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Observatory
|
||||||
|
{
|
||||||
|
class ObservatoryCore
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AppBuilder BuildAvaloniaApp()
|
||||||
|
{
|
||||||
|
return AppBuilder.Configure<UI.MainApplication>()
|
||||||
|
.UsePlatformDetect()
|
||||||
|
.LogToTrace()
|
||||||
|
.UseReactiveUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
ObservatoryCore/ObservatoryCore.csproj
Normal file
59
ObservatoryCore/ObservatoryCore.csproj
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<RootNamespace>Observatory</RootNamespace>
|
||||||
|
<SignAssembly>true</SignAssembly>
|
||||||
|
<DelaySign>false</DelaySign>
|
||||||
|
<AssemblyOriginatorKeyFile>ObservatoryKey.snk</AssemblyOriginatorKeyFile>
|
||||||
|
<!--<PublishTrimmed>true</PublishTrimmed>-->
|
||||||
|
<TrimMode>Link</TrimMode>
|
||||||
|
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Avalonia" Version="0.10.3" />
|
||||||
|
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.3" />
|
||||||
|
<PackageReference Include="Avalonia.Desktop" Version="0.10.3" />
|
||||||
|
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.3" />
|
||||||
|
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.3" />
|
||||||
|
<PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="UI\Assets\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="ObservatoryFramework">
|
||||||
|
<HintPath>..\..\ObservatoryFramework\ObservatoryFramework\bin\Release\net5.0\ObservatoryFramework.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Properties\Core.Designer.cs">
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Core.settings</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Update="UI\Views\CoreView.axaml.cs">
|
||||||
|
<DependentUpon>CoreView.axaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Update="UI\Views\NotificationView.axaml.cs">
|
||||||
|
<DependentUpon>NotificationView.axaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Properties\Core.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Core.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
38
ObservatoryCore/PluginManagement/PlaceholderPlugin.cs
Normal file
38
ObservatoryCore/PluginManagement/PlaceholderPlugin.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using Observatory.Framework;
|
||||||
|
using Observatory.Framework.Files;
|
||||||
|
using Observatory.Framework.Files.Journal;
|
||||||
|
using Observatory.Framework.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Observatory.PluginManagement
|
||||||
|
{
|
||||||
|
public class PlaceholderPlugin : IObservatoryNotifier
|
||||||
|
{
|
||||||
|
public PlaceholderPlugin(string name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name => name;
|
||||||
|
|
||||||
|
private string name;
|
||||||
|
|
||||||
|
public string ShortName => name;
|
||||||
|
|
||||||
|
public string Version => string.Empty;
|
||||||
|
|
||||||
|
public PluginUI PluginUI => new PluginUI(PluginUI.UIType.None, null);
|
||||||
|
|
||||||
|
public object Settings { get => null; set { } }
|
||||||
|
|
||||||
|
public void Load(IObservatoryCore observatoryCore)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public void OnNotificationEvent(string title, string text)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
}
|
86
ObservatoryCore/PluginManagement/PluginCore.cs
Normal file
86
ObservatoryCore/PluginManagement/PluginCore.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
using Observatory.Framework;
|
||||||
|
using Observatory.Framework.Files;
|
||||||
|
using Observatory.Framework.Interfaces;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Observatory.PluginManagement
|
||||||
|
{
|
||||||
|
public class PluginCore : IObservatoryCore
|
||||||
|
{
|
||||||
|
|
||||||
|
public string Version => "1.0a";
|
||||||
|
|
||||||
|
public Status GetStatus()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RequestAllJournals()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RequestJournalRange(DateTime start, DateTime end)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RequestJournalRange(int startIndex, int number, bool newestFirst)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendNotification(string title, string text)
|
||||||
|
{
|
||||||
|
if (!LogMonitor.GetInstance.ReadAllInProgress())
|
||||||
|
{
|
||||||
|
var handler = Notification;
|
||||||
|
handler?.Invoke(this, new NotificationEventArgs() { Title = title, Detail = text });
|
||||||
|
|
||||||
|
if (Properties.Core.Default.NativeNotify)
|
||||||
|
{
|
||||||
|
InvokeNativeNotification(title, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InvokeNativeNotification(string title, string text)
|
||||||
|
{
|
||||||
|
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
var notifyWindow = new UI.Views.NotificationView() { DataContext = new UI.ViewModels.NotificationViewModel(title, text) };
|
||||||
|
notifyWindow.Show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddGridItem(IObservatoryWorker worker, object item)
|
||||||
|
{
|
||||||
|
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
worker.PluginUI.DataGrid.Add(item);
|
||||||
|
|
||||||
|
//Hacky removal of original empty object if one was used to populate columns
|
||||||
|
if (worker.PluginUI.DataGrid.Count == 2)
|
||||||
|
{
|
||||||
|
bool allNull = true;
|
||||||
|
Type itemType = worker.PluginUI.DataGrid[0].GetType();
|
||||||
|
foreach(var property in itemType.GetProperties())
|
||||||
|
{
|
||||||
|
if (property.GetValue(worker.PluginUI.DataGrid[0], null) != null)
|
||||||
|
{
|
||||||
|
allNull = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allNull)
|
||||||
|
worker.PluginUI.DataGrid.RemoveAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<NotificationEventArgs> Notification;
|
||||||
|
}
|
||||||
|
}
|
48
ObservatoryCore/PluginManagement/PluginEventHandler.cs
Normal file
48
ObservatoryCore/PluginManagement/PluginEventHandler.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using Observatory.Framework;
|
||||||
|
using Observatory.Framework.Interfaces;
|
||||||
|
using Observatory.Framework.Files;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Observatory.Framework.Files.Journal;
|
||||||
|
|
||||||
|
namespace Observatory.PluginManagement
|
||||||
|
{
|
||||||
|
class PluginEventHandler
|
||||||
|
{
|
||||||
|
private IEnumerable<IObservatoryWorker> observatoryWorkers;
|
||||||
|
private IEnumerable<IObservatoryNotifier> observatoryNotifiers;
|
||||||
|
|
||||||
|
public PluginEventHandler(IEnumerable<IObservatoryWorker> observatoryWorkers, IEnumerable<IObservatoryNotifier> observatoryNotifiers)
|
||||||
|
{
|
||||||
|
this.observatoryWorkers = observatoryWorkers;
|
||||||
|
this.observatoryNotifiers = observatoryNotifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnJournalEvent(object source, JournalEventArgs journalEventArgs)
|
||||||
|
{
|
||||||
|
foreach (var worker in observatoryWorkers)
|
||||||
|
{
|
||||||
|
worker.JournalEvent((JournalBase)journalEventArgs.journalEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnStatusUpdate(object sourece, JournalEventArgs journalEventArgs)
|
||||||
|
{
|
||||||
|
foreach (var worker in observatoryWorkers)
|
||||||
|
{
|
||||||
|
worker.StatusChange((Status)journalEventArgs.journalEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnNotificationEvent(object source, NotificationEventArgs notificationEventArgs)
|
||||||
|
{
|
||||||
|
foreach (var notifier in observatoryNotifiers)
|
||||||
|
{
|
||||||
|
notifier.OnNotificationEvent(notificationEventArgs.Title, notificationEventArgs.Detail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
282
ObservatoryCore/PluginManagement/PluginManager.cs
Normal file
282
ObservatoryCore/PluginManagement/PluginManager.cs
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Data;
|
||||||
|
using Observatory.Framework.Interfaces;
|
||||||
|
using System.IO;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace Observatory.PluginManagement
|
||||||
|
{
|
||||||
|
public class PluginManager
|
||||||
|
{
|
||||||
|
public static PluginManager GetInstance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _instance.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Lazy<PluginManager> _instance = new Lazy<PluginManager>(NewPluginManager);
|
||||||
|
|
||||||
|
private static PluginManager NewPluginManager()
|
||||||
|
{
|
||||||
|
return new PluginManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public readonly List<string> errorList;
|
||||||
|
public readonly List<Panel> pluginPanels;
|
||||||
|
public readonly List<DataTable> pluginTables;
|
||||||
|
public readonly List<(IObservatoryWorker plugin, PluginStatus signed)> workerPlugins;
|
||||||
|
public readonly List<(IObservatoryNotifier plugin, PluginStatus signed)> notifyPlugins;
|
||||||
|
|
||||||
|
private PluginManager()
|
||||||
|
{
|
||||||
|
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
|
||||||
|
errorList = LoadPlugins(out workerPlugins, out notifyPlugins);
|
||||||
|
|
||||||
|
foreach (var error in errorList)
|
||||||
|
{
|
||||||
|
Console.WriteLine(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pluginHandler = new PluginEventHandler(workerPlugins.Select(p => p.plugin), notifyPlugins.Select(p => p.plugin));
|
||||||
|
var logMonitor = LogMonitor.GetInstance;
|
||||||
|
pluginPanels = new();
|
||||||
|
pluginTables = new();
|
||||||
|
|
||||||
|
logMonitor.JournalEntry += pluginHandler.OnJournalEvent;
|
||||||
|
logMonitor.StatusUpdate += pluginHandler.OnStatusUpdate;
|
||||||
|
|
||||||
|
var core = new PluginCore();
|
||||||
|
|
||||||
|
foreach (var plugin in workerPlugins.Select(p => p.plugin))
|
||||||
|
{
|
||||||
|
LoadSettings(plugin);
|
||||||
|
plugin.Load(core);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.Notification += pluginHandler.OnNotificationEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadSettings(IObservatoryPlugin plugin)
|
||||||
|
{
|
||||||
|
string settingsFile = GetSettingsFile(plugin);
|
||||||
|
bool createFile = !File.Exists(settingsFile);
|
||||||
|
|
||||||
|
if (!createFile)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string settingsJson = File.ReadAllText(settingsFile);
|
||||||
|
if (settingsJson != "null")
|
||||||
|
plugin.Settings = JsonSerializer.Deserialize(settingsJson, plugin.Settings.GetType());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
//Invalid settings file, remove and recreate
|
||||||
|
File.Delete(settingsFile);
|
||||||
|
createFile = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (createFile)
|
||||||
|
{
|
||||||
|
string settingsJson = JsonSerializer.Serialize(plugin.Settings);
|
||||||
|
string settingsDirectory = new FileInfo(settingsFile).DirectoryName;
|
||||||
|
if (!Directory.Exists(settingsDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(settingsDirectory);
|
||||||
|
}
|
||||||
|
File.WriteAllText(settingsFile, settingsJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<PropertyInfo, string> GetSettingDisplayNames(object settings)
|
||||||
|
{
|
||||||
|
var settingNames = new Dictionary<PropertyInfo, string>();
|
||||||
|
|
||||||
|
var properties = settings.GetType().GetProperties();
|
||||||
|
foreach (var property in properties)
|
||||||
|
{
|
||||||
|
var attrib = property.GetCustomAttribute<Framework.SettingDisplayName>();
|
||||||
|
if (attrib == null)
|
||||||
|
{
|
||||||
|
settingNames.Add(property, property.Name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
settingNames.Add(property, attrib.DisplayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return settingNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveSettings(IObservatoryPlugin plugin, object settings)
|
||||||
|
{
|
||||||
|
string settingsFile = GetSettingsFile(plugin);
|
||||||
|
|
||||||
|
string settingsJson = JsonSerializer.Serialize(settings, new JsonSerializerOptions()
|
||||||
|
{
|
||||||
|
ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.Preserve
|
||||||
|
});
|
||||||
|
string settingsDirectory = new FileInfo(settingsFile).DirectoryName;
|
||||||
|
if (!Directory.Exists(settingsDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(settingsDirectory);
|
||||||
|
}
|
||||||
|
File.WriteAllText(settingsFile, settingsJson);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetSettingsFile(IObservatoryPlugin plugin)
|
||||||
|
{
|
||||||
|
var configDirectory = new FileInfo(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath).Directory;
|
||||||
|
return configDirectory.FullName + "\\" + plugin.Name + ".json";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
||||||
|
{
|
||||||
|
AppDomain appDomain = (AppDomain)sender;
|
||||||
|
var assemblies = appDomain.GetAssemblies();
|
||||||
|
|
||||||
|
if (args.Name.ToLower().Contains(".resources"))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assemblies.FirstOrDefault(x => x.FullName == args.Name) == null)
|
||||||
|
{
|
||||||
|
AssemblyName assemblyName = new AssemblyName(args.Name);
|
||||||
|
System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyName(assemblyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var assembly = assemblies.FirstOrDefault(x => x.FullName == args.Name);
|
||||||
|
return assembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> LoadPlugins(out List<(IObservatoryWorker plugin, PluginStatus signed)> observatoryWorkers, out List<(IObservatoryNotifier plugin, PluginStatus signed)> observatoryNotifiers)
|
||||||
|
{
|
||||||
|
observatoryWorkers = new();
|
||||||
|
observatoryNotifiers = new();
|
||||||
|
var errorList = new List<string>();
|
||||||
|
|
||||||
|
string pluginPath = $".{Path.DirectorySeparatorChar}plugins";
|
||||||
|
|
||||||
|
if (Directory.Exists(pluginPath))
|
||||||
|
{
|
||||||
|
|
||||||
|
var pluginLibraries = Directory.GetFiles($".{Path.DirectorySeparatorChar}plugins", "*.dll");
|
||||||
|
var coreToken = Assembly.GetExecutingAssembly().GetName().GetPublicKeyToken();
|
||||||
|
foreach (var dll in pluginLibraries)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pluginToken = AssemblyName.GetAssemblyName(dll).GetPublicKeyToken();
|
||||||
|
PluginStatus signed;
|
||||||
|
|
||||||
|
if (pluginToken.Length == 0)
|
||||||
|
{
|
||||||
|
errorList.Add($"Warning: {dll} not signed.");
|
||||||
|
signed = PluginStatus.Unsigned;
|
||||||
|
}
|
||||||
|
else if (!coreToken.SequenceEqual(pluginToken))
|
||||||
|
{
|
||||||
|
errorList.Add($"Warning: {dll} signature does not match.");
|
||||||
|
signed = PluginStatus.InvalidSignature;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorList.Add($"OK: {dll} signed.");
|
||||||
|
signed = PluginStatus.Signed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signed == PluginStatus.Signed || Properties.Core.Default.AllowUnsigned)
|
||||||
|
{
|
||||||
|
string error = LoadPluginAssembly(dll, observatoryWorkers, observatoryNotifiers);
|
||||||
|
if (!string.IsNullOrWhiteSpace(error))
|
||||||
|
{
|
||||||
|
errorList.Add(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LoadPlaceholderPlugin(dll, signed, observatoryNotifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorList.Add($"ERROR: {new FileInfo(dll).Name}, {ex.Message}");
|
||||||
|
LoadPlaceholderPlugin(dll, PluginStatus.InvalidLibrary, observatoryNotifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errorList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string LoadPluginAssembly(string dllPath, List<(IObservatoryWorker plugin, PluginStatus signed)> workers, List<(IObservatoryNotifier plugin, PluginStatus signed)> notifiers)
|
||||||
|
{
|
||||||
|
var pluginAssembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(new FileInfo(dllPath).FullName);
|
||||||
|
Type[] types;
|
||||||
|
string err = string.Empty;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
types = pluginAssembly.GetTypes();
|
||||||
|
}
|
||||||
|
catch (ReflectionTypeLoadException ex)
|
||||||
|
{
|
||||||
|
types = ex.Types.Where(t => t != null).ToArray();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
types = Array.Empty<Type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var workerTypes = types.Where(t => t.IsAssignableTo(typeof(IObservatoryWorker)));
|
||||||
|
foreach (var worker in workerTypes)
|
||||||
|
{
|
||||||
|
ConstructorInfo constructor = worker.GetConstructor(Array.Empty<Type>());
|
||||||
|
object instance = constructor.Invoke(Array.Empty<object>());
|
||||||
|
workers.Add((instance as IObservatoryWorker, PluginStatus.Signed));
|
||||||
|
}
|
||||||
|
|
||||||
|
var notifyTypes = types.Where(t => t.IsAssignableTo(typeof(IObservatoryNotifier)));
|
||||||
|
foreach (var notifier in notifyTypes)
|
||||||
|
{
|
||||||
|
ConstructorInfo constructor = notifier.GetConstructor(Array.Empty<Type>());
|
||||||
|
object instance = constructor.Invoke(Array.Empty<object>());
|
||||||
|
notifiers.Add((instance as IObservatoryNotifier, PluginStatus.Signed));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workerTypes.Count() + notifyTypes.Count() == 0)
|
||||||
|
{
|
||||||
|
err += $"ERROR: Library '{dllPath}' contains no suitable interfaces.";
|
||||||
|
LoadPlaceholderPlugin(dllPath, PluginStatus.InvalidPlugin, notifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadPlaceholderPlugin(string dllPath, PluginStatus pluginStatus, List<(IObservatoryNotifier plugin, PluginStatus signed)> notifiers)
|
||||||
|
{
|
||||||
|
PlaceholderPlugin placeholder = new(new FileInfo(dllPath).Name);
|
||||||
|
notifiers.Add((placeholder, pluginStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PluginStatus
|
||||||
|
{
|
||||||
|
Signed,
|
||||||
|
Unsigned,
|
||||||
|
InvalidSignature,
|
||||||
|
InvalidPlugin,
|
||||||
|
InvalidLibrary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
ObservatoryCore/Properties/Core.Designer.cs
generated
Normal file
62
ObservatoryCore/Properties/Core.Designer.cs
generated
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Observatory.Properties {
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")]
|
||||||
|
internal sealed partial class Core : global::System.Configuration.ApplicationSettingsBase {
|
||||||
|
|
||||||
|
private static Core defaultInstance = ((Core)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Core())));
|
||||||
|
|
||||||
|
public static Core Default {
|
||||||
|
get {
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||||
|
public string JournalFolder {
|
||||||
|
get {
|
||||||
|
return ((string)(this["JournalFolder"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["JournalFolder"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||||
|
public bool AllowUnsigned {
|
||||||
|
get {
|
||||||
|
return ((bool)(this["AllowUnsigned"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["AllowUnsigned"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||||
|
public bool NativeNotify {
|
||||||
|
get {
|
||||||
|
return ((bool)(this["NativeNotify"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["NativeNotify"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
ObservatoryCore/Properties/Core.settings
Normal file
15
ObservatoryCore/Properties/Core.settings
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="Observatory.Properties" GeneratedClassName="Core">
|
||||||
|
<Profiles />
|
||||||
|
<Settings>
|
||||||
|
<Setting Name="JournalFolder" Type="System.String" Scope="User">
|
||||||
|
<Value Profile="(Default)" />
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="AllowUnsigned" Type="System.Boolean" Scope="User">
|
||||||
|
<Value Profile="(Default)">False</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="NativeNotify" Type="System.Boolean" Scope="User">
|
||||||
|
<Value Profile="(Default)">True</Value>
|
||||||
|
</Setting>
|
||||||
|
</Settings>
|
||||||
|
</SettingsFile>
|
13
ObservatoryCore/UI/MainApplication.axaml
Normal file
13
ObservatoryCore/UI/MainApplication.axaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<Application xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="clr-namespace:Observatory.UI"
|
||||||
|
x:Class="Observatory.UI.MainApplication">
|
||||||
|
<Application.DataTemplates>
|
||||||
|
<local:ViewLocator/>
|
||||||
|
</Application.DataTemplates>
|
||||||
|
<Application.Styles>
|
||||||
|
<FluentTheme Mode="Dark"/>
|
||||||
|
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
|
||||||
|
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default"/>
|
||||||
|
</Application.Styles>
|
||||||
|
</Application>
|
29
ObservatoryCore/UI/MainApplication.axaml.cs
Normal file
29
ObservatoryCore/UI/MainApplication.axaml.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Observatory.UI.ViewModels;
|
||||||
|
|
||||||
|
namespace Observatory.UI
|
||||||
|
{
|
||||||
|
public class MainApplication : Application
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
{
|
||||||
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
var pluginManager = PluginManagement.PluginManager.GetInstance;
|
||||||
|
desktop.MainWindow = new Views.MainWindow()
|
||||||
|
{
|
||||||
|
DataContext = new MainWindowViewModel(pluginManager)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnFrameworkInitializationCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
ObservatoryCore/UI/Models/BasicUIModel.cs
Normal file
14
ObservatoryCore/UI/Models/BasicUIModel.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Observatory.UI.Models
|
||||||
|
{
|
||||||
|
public class BasicUIModel
|
||||||
|
{
|
||||||
|
public string Time { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
}
|
||||||
|
}
|
17
ObservatoryCore/UI/Models/CoreModel.cs
Normal file
17
ObservatoryCore/UI/Models/CoreModel.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Observatory.UI.ViewModels;
|
||||||
|
|
||||||
|
namespace Observatory.UI.Models
|
||||||
|
{
|
||||||
|
public class CoreModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public ViewModelBase UI { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
14
ObservatoryCore/UI/Models/NotificationModel.cs
Normal file
14
ObservatoryCore/UI/Models/NotificationModel.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Observatory.UI.Models
|
||||||
|
{
|
||||||
|
public class NotificationModel
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Detail { get; set; }
|
||||||
|
}
|
||||||
|
}
|
31
ObservatoryCore/UI/TabTemplateSelector.cs
Normal file
31
ObservatoryCore/UI/TabTemplateSelector.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
using Avalonia.Metadata;
|
||||||
|
using Observatory.UI.Views;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Observatory.UI
|
||||||
|
{
|
||||||
|
public class TabTemplateSelector : IDataTemplate
|
||||||
|
{
|
||||||
|
public bool SupportsRecycling => false;
|
||||||
|
|
||||||
|
[Content]
|
||||||
|
public Dictionary<string, IDataTemplate> Templates { get; } = new Dictionary<string, IDataTemplate>();
|
||||||
|
|
||||||
|
|
||||||
|
public IControl Build(object param)
|
||||||
|
{
|
||||||
|
return new BasicUIView(); //Templates[param].Build(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(object data)
|
||||||
|
{
|
||||||
|
return data is BasicUIView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
ObservatoryCore/UI/ViewLocator.cs
Normal file
32
ObservatoryCore/UI/ViewLocator.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
using Observatory.UI.ViewModels;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Observatory.UI
|
||||||
|
{
|
||||||
|
public class ViewLocator : IDataTemplate
|
||||||
|
{
|
||||||
|
public bool SupportsRecycling => false;
|
||||||
|
|
||||||
|
public IControl Build(object data)
|
||||||
|
{
|
||||||
|
var name = data.GetType().FullName!.Replace("ViewModel", "View");
|
||||||
|
var type = Type.GetType(name);
|
||||||
|
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
return (Control)Activator.CreateInstance(type)!;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new TextBlock { Text = "Not Found: " + name };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(object data)
|
||||||
|
{
|
||||||
|
return data is ViewModelBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
ObservatoryCore/UI/ViewModels/BasicUIViewModel.cs
Normal file
67
ObservatoryCore/UI/ViewModels/BasicUIViewModel.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Data;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using Observatory.UI.Models;
|
||||||
|
using ReactiveUI;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using Observatory.Framework;
|
||||||
|
|
||||||
|
namespace Observatory.UI.ViewModels
|
||||||
|
{
|
||||||
|
public class BasicUIViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private ObservableCollection<object> basicUIGrid;
|
||||||
|
|
||||||
|
|
||||||
|
public ObservableCollection<object> BasicUIGrid
|
||||||
|
{
|
||||||
|
get => basicUIGrid;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
basicUIGrid = value;
|
||||||
|
this.RaisePropertyChanged(nameof(BasicUIGrid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasicUIViewModel(ObservableCollection<object> BasicUIGrid)
|
||||||
|
{
|
||||||
|
|
||||||
|
this.BasicUIGrid = new();
|
||||||
|
this.BasicUIGrid = BasicUIGrid;
|
||||||
|
|
||||||
|
//// Create a timer and set a two second interval.
|
||||||
|
//var aTimer = new System.Timers.Timer();
|
||||||
|
//aTimer.Interval = 2000;
|
||||||
|
|
||||||
|
//// Hook up the Elapsed event for the timer.
|
||||||
|
//aTimer.Elapsed += OnTimedEvent;
|
||||||
|
|
||||||
|
//// Have the timer fire repeated events (true is the default)
|
||||||
|
//aTimer.AutoReset = true;
|
||||||
|
|
||||||
|
//// Start the timer
|
||||||
|
//aTimer.Enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PluginUI.UIType uiType;
|
||||||
|
|
||||||
|
public PluginUI.UIType UIType
|
||||||
|
{
|
||||||
|
get => uiType;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
uiType = value;
|
||||||
|
this.RaisePropertyChanged(nameof(UIType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTimedEvent(object sender, System.Timers.ElapsedEventArgs e)
|
||||||
|
{
|
||||||
|
basicUIGrid.Count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
110
ObservatoryCore/UI/ViewModels/CoreViewModel.cs
Normal file
110
ObservatoryCore/UI/ViewModels/CoreViewModel.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Observatory.Framework.Interfaces;
|
||||||
|
using Observatory.UI.Models;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Observatory.UI.ViewModels
|
||||||
|
{
|
||||||
|
public class CoreViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private readonly ObservableCollection<IObservatoryNotifier> notifiers;
|
||||||
|
private readonly ObservableCollection<IObservatoryWorker> workers;
|
||||||
|
private readonly ObservableCollection<CoreModel> tabs;
|
||||||
|
private string toggleButtonText;
|
||||||
|
|
||||||
|
public CoreViewModel(IEnumerable<(IObservatoryWorker plugin, PluginManagement.PluginManager.PluginStatus signed)> workers, IEnumerable<(IObservatoryNotifier plugin, PluginManagement.PluginManager.PluginStatus signed)> notifiers)
|
||||||
|
{
|
||||||
|
this.notifiers = new ObservableCollection<IObservatoryNotifier>(notifiers.Select(p => p.plugin));
|
||||||
|
this.workers = new ObservableCollection<IObservatoryWorker>(workers.Select(p => p.plugin));
|
||||||
|
ToggleButtonText = "Start Monitor";
|
||||||
|
tabs = new ObservableCollection<CoreModel>();
|
||||||
|
|
||||||
|
foreach(var worker in workers.Select(p => p.plugin))
|
||||||
|
{
|
||||||
|
if (worker.PluginUI.PluginUIType == Framework.PluginUI.UIType.Basic)
|
||||||
|
{
|
||||||
|
CoreModel coreModel = new();
|
||||||
|
coreModel.Name = worker.ShortName;
|
||||||
|
coreModel.UI = new();
|
||||||
|
var uiViewModel = new BasicUIViewModel(worker.PluginUI.DataGrid)
|
||||||
|
{
|
||||||
|
UIType = worker.PluginUI.PluginUIType
|
||||||
|
};
|
||||||
|
coreModel.UI = uiViewModel;
|
||||||
|
|
||||||
|
tabs.Add(coreModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(var notifier in notifiers.Select(p => p.plugin))
|
||||||
|
{
|
||||||
|
Panel notifierPanel = new Panel();
|
||||||
|
TextBlock notifierTextBlock = new TextBlock();
|
||||||
|
notifierTextBlock.Text = notifier.Name;
|
||||||
|
notifierPanel.Children.Add(notifierTextBlock);
|
||||||
|
//tabs.Add(new CoreModel() { Name = notifier.ShortName, UI = (ViewModelBase)notifier.UI });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tabs.Add(new CoreModel() { Name = "Core", UI = new BasicUIViewModel(new ObservableCollection<object>()) { UIType = Framework.PluginUI.UIType.Core } });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReadAll()
|
||||||
|
{
|
||||||
|
foreach (var worker in workers)
|
||||||
|
{
|
||||||
|
worker.ReadAllStarted();
|
||||||
|
}
|
||||||
|
LogMonitor.GetInstance.ReadAllJournals();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToggleMonitor()
|
||||||
|
{
|
||||||
|
var logMonitor = LogMonitor.GetInstance;
|
||||||
|
|
||||||
|
if (logMonitor.IsMonitoring())
|
||||||
|
{
|
||||||
|
logMonitor.Stop();
|
||||||
|
ToggleButtonText = "Start Monitor";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logMonitor.Start();
|
||||||
|
ToggleButtonText = "Stop Monitor";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ToggleButtonText
|
||||||
|
{
|
||||||
|
get => toggleButtonText;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (toggleButtonText != value)
|
||||||
|
{
|
||||||
|
toggleButtonText = value;
|
||||||
|
this.RaisePropertyChanged(nameof(ToggleButtonText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<IObservatoryWorker> Workers
|
||||||
|
{
|
||||||
|
get { return workers; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<IObservatoryNotifier> Notifiers
|
||||||
|
{
|
||||||
|
get { return notifiers; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<CoreModel> Tabs
|
||||||
|
{
|
||||||
|
get { return tabs; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
ObservatoryCore/UI/ViewModels/MainWindowViewModel.cs
Normal file
16
ObservatoryCore/UI/ViewModels/MainWindowViewModel.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Observatory.UI.ViewModels
|
||||||
|
{
|
||||||
|
public class MainWindowViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
public MainWindowViewModel(PluginManagement.PluginManager pluginManager)
|
||||||
|
{
|
||||||
|
core = new CoreViewModel(pluginManager.workerPlugins, pluginManager.notifyPlugins);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CoreViewModel core { get; }
|
||||||
|
}
|
||||||
|
}
|
18
ObservatoryCore/UI/ViewModels/NotificationViewModel.cs
Normal file
18
ObservatoryCore/UI/ViewModels/NotificationViewModel.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Observatory.UI.ViewModels
|
||||||
|
{
|
||||||
|
public class NotificationViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
public NotificationViewModel(string title, string detail)
|
||||||
|
{
|
||||||
|
Notification = new() { Title = title, Detail = detail };
|
||||||
|
}
|
||||||
|
|
||||||
|
public Models.NotificationModel Notification;
|
||||||
|
}
|
||||||
|
}
|
11
ObservatoryCore/UI/ViewModels/ViewModelBase.cs
Normal file
11
ObservatoryCore/UI/ViewModels/ViewModelBase.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using ReactiveUI;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Observatory.UI.ViewModels
|
||||||
|
{
|
||||||
|
public class ViewModelBase : ReactiveObject
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
10
ObservatoryCore/UI/Views/BasicUIView.axaml
Normal file
10
ObservatoryCore/UI/Views/BasicUIView.axaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Observatory.UI.Views.BasicUIView">
|
||||||
|
<Panel Name="UIPanel">
|
||||||
|
|
||||||
|
</Panel>
|
||||||
|
</UserControl>
|
404
ObservatoryCore/UI/Views/BasicUIView.axaml.cs
Normal file
404
ObservatoryCore/UI/Views/BasicUIView.axaml.cs
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Observatory.Framework;
|
||||||
|
using Observatory.Framework.Interfaces;
|
||||||
|
using System.Linq;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
|
|
||||||
|
namespace Observatory.UI.Views
|
||||||
|
{
|
||||||
|
public class BasicUIView : UserControl
|
||||||
|
{
|
||||||
|
public BasicUIView()
|
||||||
|
{
|
||||||
|
Initialized += OnInitialized;
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DirectProperty<BasicUIView, PluginUI.UIType> UITypeProperty =
|
||||||
|
AvaloniaProperty.RegisterDirect<BasicUIView, PluginUI.UIType>(
|
||||||
|
nameof(UIType),
|
||||||
|
o => o.UIType,
|
||||||
|
(o, v) => o.UIType = v,
|
||||||
|
PluginUI.UIType.None,
|
||||||
|
BindingMode.OneWay
|
||||||
|
);
|
||||||
|
|
||||||
|
public PluginUI.UIType UIType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _uitype;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_uitype = value;
|
||||||
|
UITypeChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PluginUI.UIType _uitype;
|
||||||
|
|
||||||
|
|
||||||
|
private void ColumnGeneration(object sender, DataGridAutoGeneratingColumnEventArgs e)
|
||||||
|
{
|
||||||
|
e.Column.Header = SplitCamelCase(e.PropertyName);
|
||||||
|
e.Column.CanUserReorder = true;
|
||||||
|
e.Column.CanUserResize = true;
|
||||||
|
e.Column.CanUserSort = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInitialized(object sender, System.EventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UITypeChange()
|
||||||
|
{
|
||||||
|
var uiPanel = this.Find<Panel>("UIPanel");
|
||||||
|
|
||||||
|
switch (UIType)
|
||||||
|
{
|
||||||
|
case PluginUI.UIType.None:
|
||||||
|
break;
|
||||||
|
case PluginUI.UIType.Basic:
|
||||||
|
DataGrid dataGrid = new()
|
||||||
|
{
|
||||||
|
[!DataGrid.ItemsProperty] = new Binding("BasicUIGrid"),
|
||||||
|
SelectionMode = DataGridSelectionMode.Extended,
|
||||||
|
GridLinesVisibility = DataGridGridLinesVisibility.Vertical,
|
||||||
|
AutoGenerateColumns = true
|
||||||
|
};
|
||||||
|
dataGrid.AutoGeneratingColumn += ColumnGeneration;
|
||||||
|
uiPanel.Children.Clear();
|
||||||
|
uiPanel.Children.Add(dataGrid);
|
||||||
|
break;
|
||||||
|
case PluginUI.UIType.Avalonia:
|
||||||
|
break;
|
||||||
|
case PluginUI.UIType.Core:
|
||||||
|
uiPanel.Children.Clear();
|
||||||
|
ScrollViewer scrollViewer = new();
|
||||||
|
scrollViewer.Content = GenerateCoreUI();
|
||||||
|
uiPanel.Children.Add(scrollViewer);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Grid GenerateCoreUI()
|
||||||
|
{
|
||||||
|
|
||||||
|
Grid corePanel = new();
|
||||||
|
|
||||||
|
ColumnDefinitions columns = new()
|
||||||
|
{
|
||||||
|
new ColumnDefinition() { Width = new GridLength(0, GridUnitType.Auto) },
|
||||||
|
new ColumnDefinition() { Width = new GridLength(300) },
|
||||||
|
new ColumnDefinition() { Width = new GridLength(0, GridUnitType.Auto) }
|
||||||
|
};
|
||||||
|
corePanel.ColumnDefinitions = columns;
|
||||||
|
|
||||||
|
RowDefinitions rows = new()
|
||||||
|
{
|
||||||
|
new RowDefinition() { Height = new GridLength(0, GridUnitType.Auto) },
|
||||||
|
new RowDefinition() { Height = new GridLength(0, GridUnitType.Auto) }
|
||||||
|
};
|
||||||
|
corePanel.RowDefinitions = rows;
|
||||||
|
|
||||||
|
var pluginManager = PluginManagement.PluginManager.GetInstance;
|
||||||
|
|
||||||
|
#region Journal Location
|
||||||
|
TextBlock journalPathLabel = new()
|
||||||
|
{
|
||||||
|
Text = "Journal Path: ",
|
||||||
|
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right,
|
||||||
|
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
TextBox journalPath = new()
|
||||||
|
{
|
||||||
|
Text = Properties.Core.Default.JournalFolder
|
||||||
|
};
|
||||||
|
|
||||||
|
Button journalBrowse = new()
|
||||||
|
{
|
||||||
|
Content = "Browse",
|
||||||
|
Height = 30,
|
||||||
|
Width = 100,
|
||||||
|
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right,
|
||||||
|
HorizontalContentAlignment = Avalonia.Layout.HorizontalAlignment.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
journalBrowse.Click += (object source, RoutedEventArgs e) =>
|
||||||
|
{
|
||||||
|
OpenFolderDialog openFolderDialog = new()
|
||||||
|
{
|
||||||
|
Directory = journalPath.Text
|
||||||
|
};
|
||||||
|
var browseTask = openFolderDialog.ShowAsync((Window)((Button)source).GetVisualRoot());
|
||||||
|
string path = browseTask.Result;
|
||||||
|
if (path != string.Empty)
|
||||||
|
{
|
||||||
|
journalPath.Text = path;
|
||||||
|
Properties.Core.Default.JournalFolder = path;
|
||||||
|
Properties.Core.Default.Save();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
corePanel.AddControl(journalPathLabel, 1, 0);
|
||||||
|
corePanel.AddControl(journalPath, 1, 1);
|
||||||
|
corePanel.AddControl(journalBrowse, 1, 2);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Plugin List
|
||||||
|
DataGrid pluginList = new() { Margin = new Thickness(0, 20) };
|
||||||
|
|
||||||
|
pluginList.Columns.Add(new DataGridTextColumn()
|
||||||
|
{
|
||||||
|
Header = "Plugin",
|
||||||
|
Binding = new Binding("Name")
|
||||||
|
});
|
||||||
|
|
||||||
|
pluginList.Columns.Add(new DataGridTextColumn()
|
||||||
|
{
|
||||||
|
Header = "Version",
|
||||||
|
Binding = new Binding("Version")
|
||||||
|
});
|
||||||
|
|
||||||
|
pluginList.Columns.Add(new DataGridTextColumn()
|
||||||
|
{
|
||||||
|
Header = "Status",
|
||||||
|
Binding = new Binding("Status")
|
||||||
|
});
|
||||||
|
|
||||||
|
System.Collections.Generic.List<PluginView> allPlugins = new();
|
||||||
|
|
||||||
|
foreach(var (plugin, signed) in pluginManager.workerPlugins)
|
||||||
|
{
|
||||||
|
allPlugins.Add(new PluginView() { Name = plugin.Name, Version = plugin.Version, Status = GetStatusText(signed) });
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (plugin, signed) in pluginManager.notifyPlugins)
|
||||||
|
{
|
||||||
|
allPlugins.Add(new PluginView() { Name = plugin.Name, Version = plugin.Version, Status = GetStatusText(signed) });
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginList.Items = allPlugins;
|
||||||
|
corePanel.AddControl(pluginList, 0, 0, 2);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Plugin Settings
|
||||||
|
|
||||||
|
foreach(var plugin in pluginManager.workerPlugins.Select(p => p.plugin))
|
||||||
|
{
|
||||||
|
GeneratePluginSettingUI(corePanel, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
return corePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GeneratePluginSettingUI(Grid gridPanel, IObservatoryPlugin plugin)
|
||||||
|
{
|
||||||
|
//var plugin = pluginSettings.Key;
|
||||||
|
|
||||||
|
var displayedSettings = PluginManagement.PluginManager.GetSettingDisplayNames(plugin.Settings);
|
||||||
|
|
||||||
|
if (displayedSettings.Count > 0)
|
||||||
|
{
|
||||||
|
Expander expander = new()
|
||||||
|
{
|
||||||
|
Header = $"{plugin.Name} - {plugin.Version}",
|
||||||
|
DataContext = plugin.Settings,
|
||||||
|
Margin = new Thickness(0, 20)
|
||||||
|
};
|
||||||
|
|
||||||
|
Grid settingsGrid = new();
|
||||||
|
ColumnDefinitions settingColumns = new()
|
||||||
|
{
|
||||||
|
new ColumnDefinition() { Width = new GridLength(3, GridUnitType.Star) },
|
||||||
|
new ColumnDefinition() { Width = new GridLength(3, GridUnitType.Star) },
|
||||||
|
new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) }
|
||||||
|
};
|
||||||
|
settingsGrid.ColumnDefinitions = settingColumns;
|
||||||
|
expander.Content = settingsGrid;
|
||||||
|
|
||||||
|
int nextRow = gridPanel.RowDefinitions.Count;
|
||||||
|
gridPanel.RowDefinitions.Add(new RowDefinition());
|
||||||
|
gridPanel.AddControl(expander, nextRow, 0, 3);
|
||||||
|
|
||||||
|
foreach (var setting in displayedSettings.Where(s => !System.Attribute.IsDefined(s.Key, typeof(SettingIgnore))))
|
||||||
|
{
|
||||||
|
if (setting.Key.PropertyType != typeof(bool) || settingsGrid.Children.Count % 2 == 0)
|
||||||
|
{
|
||||||
|
settingsGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(21) });
|
||||||
|
}
|
||||||
|
|
||||||
|
TextBlock label = new() { Text = setting.Value };
|
||||||
|
|
||||||
|
switch (setting.Key.GetValue(plugin.Settings))
|
||||||
|
{
|
||||||
|
case bool boolSetting:
|
||||||
|
CheckBox checkBox = new() { IsChecked = boolSetting, Content = label };
|
||||||
|
|
||||||
|
checkBox.Checked += (object sender, RoutedEventArgs e) =>
|
||||||
|
{
|
||||||
|
setting.Key.SetValue(plugin.Settings, true);
|
||||||
|
PluginManagement.PluginManager.GetInstance.SaveSettings(plugin, plugin.Settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
checkBox.Unchecked += (object sender, RoutedEventArgs e) =>
|
||||||
|
{
|
||||||
|
setting.Key.SetValue(plugin.Settings, false);
|
||||||
|
PluginManagement.PluginManager.GetInstance.SaveSettings(plugin, plugin.Settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
//settingsGrid.Children.Add(checkBox);
|
||||||
|
settingsGrid.AddControl(checkBox, settingsGrid.RowDefinitions.Count - 1, settingsGrid.Children.Count % 2 == 0 ? 0 : 1);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case string stringSetting:
|
||||||
|
TextBox textBox = new() { Text = stringSetting };
|
||||||
|
settingsGrid.Children.Add(label);
|
||||||
|
settingsGrid.Children.Add(textBox);
|
||||||
|
break;
|
||||||
|
case int intSetting:
|
||||||
|
NumericUpDown numericUpDown = new() { Text = intSetting.ToString(), AllowSpin = true };
|
||||||
|
settingsGrid.Children.Add(label);
|
||||||
|
settingsGrid.Children.Add(numericUpDown);
|
||||||
|
break;
|
||||||
|
case System.IO.FileInfo fileSetting:
|
||||||
|
label.Text += ": ";
|
||||||
|
|
||||||
|
TextBox settingPath = new()
|
||||||
|
{
|
||||||
|
Text = fileSetting.FullName,
|
||||||
|
Width = 250,
|
||||||
|
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right
|
||||||
|
};
|
||||||
|
|
||||||
|
Button settingBrowse = new()
|
||||||
|
{
|
||||||
|
Content = "Browse",
|
||||||
|
Height = 30,
|
||||||
|
Width = 100,
|
||||||
|
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left,
|
||||||
|
HorizontalContentAlignment = Avalonia.Layout.HorizontalAlignment.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
settingBrowse.Click += (object source, RoutedEventArgs e) =>
|
||||||
|
{
|
||||||
|
OpenFileDialog openFileDialog = new()
|
||||||
|
{
|
||||||
|
Directory = fileSetting.DirectoryName,
|
||||||
|
AllowMultiple = false
|
||||||
|
};
|
||||||
|
var browseTask = openFileDialog.ShowAsync((Window)((Button)source).GetVisualRoot());
|
||||||
|
|
||||||
|
if (browseTask.Result.Count() > 0)
|
||||||
|
{
|
||||||
|
string path = browseTask.Result[0];
|
||||||
|
settingPath.Text = path;
|
||||||
|
|
||||||
|
setting.Key.SetValue(plugin.Settings, new System.IO.FileInfo(path));
|
||||||
|
PluginManagement.PluginManager.GetInstance.SaveSettings(plugin, plugin.Settings);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
StackPanel stackPanel = new() { Orientation = Avalonia.Layout.Orientation.Horizontal };
|
||||||
|
stackPanel.Children.Add(label);
|
||||||
|
stackPanel.Children.Add(settingPath);
|
||||||
|
|
||||||
|
settingsGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(21) });
|
||||||
|
//settingsGrid.AddControl(label, settingsGrid.RowDefinitions.Count - 1, 0, 2);
|
||||||
|
settingsGrid.AddControl(stackPanel, settingsGrid.RowDefinitions.Count - 1, 0, 2);
|
||||||
|
settingsGrid.AddControl(settingBrowse, settingsGrid.RowDefinitions.Count - 1, 2);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//wrapPanel.Children.Add(panel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetStatusText(PluginManagement.PluginManager.PluginStatus status)
|
||||||
|
{
|
||||||
|
string statusText;
|
||||||
|
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case PluginManagement.PluginManager.PluginStatus.Signed:
|
||||||
|
statusText = "Signed";
|
||||||
|
break;
|
||||||
|
case PluginManagement.PluginManager.PluginStatus.Unsigned:
|
||||||
|
statusText = "Unsigned";
|
||||||
|
break;
|
||||||
|
case PluginManagement.PluginManager.PluginStatus.InvalidSignature:
|
||||||
|
statusText = "Signature Invalid";
|
||||||
|
break;
|
||||||
|
case PluginManagement.PluginManager.PluginStatus.InvalidPlugin:
|
||||||
|
statusText = "No Interface";
|
||||||
|
break;
|
||||||
|
case PluginManagement.PluginManager.PluginStatus.InvalidLibrary:
|
||||||
|
statusText = "Invalid Library";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
statusText = "Unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return statusText;
|
||||||
|
}
|
||||||
|
|
||||||
|
//From https://stackoverflow.com/questions/5796383/insert-spaces-between-words-on-a-camel-cased-token
|
||||||
|
private static string SplitCamelCase(string str)
|
||||||
|
{
|
||||||
|
return Regex.Replace(
|
||||||
|
Regex.Replace(
|
||||||
|
str,
|
||||||
|
@"(\P{Ll})(\P{Ll}\p{Ll})",
|
||||||
|
"$1 $2"
|
||||||
|
),
|
||||||
|
@"(\p{Ll})(\P{Ll})",
|
||||||
|
"$1 $2"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class PluginView
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Version { get; set; }
|
||||||
|
public string Status { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
internal static class GridExtention
|
||||||
|
{
|
||||||
|
public static void AddControl(this Grid grid, Control control, int row, int column, int span = 1)
|
||||||
|
{
|
||||||
|
grid.Children.Add(control);
|
||||||
|
Grid.SetColumnSpan(control, span);
|
||||||
|
Grid.SetColumn(control, column);
|
||||||
|
Grid.SetRow(control, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
ObservatoryCore/UI/Views/CoreView.axaml
Normal file
43
ObservatoryCore/UI/Views/CoreView.axaml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<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:vw="clr-namespace:Observatory.UI.Views"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Observatory.UI.Views.CoreView">
|
||||||
|
<Grid RowDefinitions="30,*,Auto">
|
||||||
|
<TextBlock Grid.Row="0" VerticalAlignment="Center" Padding="10,0,0,0">
|
||||||
|
Elite Observatory - v1.0core
|
||||||
|
</TextBlock>
|
||||||
|
<TabControl Name="CoreTabs"
|
||||||
|
Grid.Row="1" Grid.Column="1"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
TabStripPlacement="Left"
|
||||||
|
Items="{Binding Tabs}"
|
||||||
|
BorderBrush="Black"
|
||||||
|
BorderThickness="1">
|
||||||
|
<TabControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TabItem>
|
||||||
|
<TabItem.Header>
|
||||||
|
<TextBlock Text="{Binding Name}"/>
|
||||||
|
</TabItem.Header>
|
||||||
|
</TabItem>
|
||||||
|
</DataTemplate>
|
||||||
|
</TabControl.ItemTemplate>
|
||||||
|
<TabControl.ContentTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<vw:BasicUIView DataContext="{Binding UI}" UIType="{Binding UIType}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</TabControl.ContentTemplate>
|
||||||
|
</TabControl>
|
||||||
|
<WrapPanel Height="50" Grid.Row="2" Grid.Column="0" VerticalAlignment="Bottom" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button Name="ToggleMonitor" Margin="10" Command="{Binding ToggleMonitor}" Content="{Binding ToggleButtonText}">
|
||||||
|
Start Monitor
|
||||||
|
</Button>
|
||||||
|
<Button Name="ReadAll" Margin="10" Command="{Binding ReadAll}">
|
||||||
|
Read All
|
||||||
|
</Button>
|
||||||
|
</WrapPanel>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
21
ObservatoryCore/UI/Views/CoreView.axaml.cs
Normal file
21
ObservatoryCore/UI/Views/CoreView.axaml.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Observatory.UI.Views
|
||||||
|
{
|
||||||
|
public class CoreView : UserControl
|
||||||
|
{
|
||||||
|
public CoreView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
ObservatoryCore/UI/Views/MainWindow.axaml
Normal file
11
ObservatoryCore/UI/Views/MainWindow.axaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:views="clr-namespace:Observatory.UI.Views"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Observatory.UI.Views.MainWindow"
|
||||||
|
Title="Elite Observatory"
|
||||||
|
ExtendClientAreaToDecorationsHint="True"
|
||||||
|
Content="{Binding core}">
|
||||||
|
</Window>
|
26
ObservatoryCore/UI/Views/MainWindow.axaml.cs
Normal file
26
ObservatoryCore/UI/Views/MainWindow.axaml.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Observatory.UI.Views
|
||||||
|
{
|
||||||
|
public class MainWindow : Window
|
||||||
|
{
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
#if DEBUG
|
||||||
|
this.AttachDevTools();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
ObservatoryCore/UI/Views/NotificationView.axaml
Normal file
13
ObservatoryCore/UI/Views/NotificationView.axaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<Window 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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Observatory.UI.Views.NotificationView"
|
||||||
|
|
||||||
|
Title="Notification">
|
||||||
|
<StackPanel DataContext="{Binding Notification}">
|
||||||
|
<TextBlock Text="{Binding Title}" />
|
||||||
|
<TextBlock Text="{Binding Detail}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Window>
|
22
ObservatoryCore/UI/Views/NotificationView.axaml.cs
Normal file
22
ObservatoryCore/UI/Views/NotificationView.axaml.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace Observatory.UI.Views
|
||||||
|
{
|
||||||
|
public partial class NotificationView : Window
|
||||||
|
{
|
||||||
|
public NotificationView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
#if DEBUG
|
||||||
|
this.AttachDevTools();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user