Add project files.
This commit is contained in:
parent
7099cf23c6
commit
a5154996ee
30 changed files with 2287 additions and 0 deletions
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; }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue