add CharacterAnalzyer, fix DtrEntry (I think)

This commit is contained in:
rootdarkarchon
2023-07-10 13:23:44 +02:00
parent 2473087da4
commit 417c08f9b2
7 changed files with 170 additions and 29 deletions

View File

@@ -1,4 +1,5 @@
using MareSynchronos.Interop; using LZ4;
using MareSynchronos.Interop;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.Utils; using MareSynchronos.Utils;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -137,6 +138,13 @@ public sealed class FileCacheManager : IDisposable
return validatedCacheEntry; return validatedCacheEntry;
} }
public async Task<(string, byte[])> GetCompressedFileData(string fileHash, CancellationToken uploadToken)
{
var fileCache = GetFileCacheByHash(fileHash)!.ResolvedFilepath;
return (fileHash, LZ4Codec.WrapHC(await File.ReadAllBytesAsync(fileCache, uploadToken).ConfigureAwait(false), 0,
(int)new FileInfo(fileCache).Length));
}
public void RemoveHashedFile(FileCacheEntity? fileCache) public void RemoveHashedFile(FileCacheEntity? fileCache)
{ {
if (fileCache == null) return; if (fileCache == null) return;

View File

@@ -72,11 +72,12 @@ public sealed class Plugin : IDalamudPlugin
collection.AddSingleton<FileDownloadManagerFactory>(); collection.AddSingleton<FileDownloadManagerFactory>();
collection.AddSingleton<PairHandlerFactory>(); collection.AddSingleton<PairHandlerFactory>();
collection.AddSingleton<PairFactory>(); collection.AddSingleton<PairFactory>();
collection.AddSingleton<CharacterAnalyzer>();
collection.AddSingleton<PluginWarningNotificationService>(); collection.AddSingleton<PluginWarningNotificationService>();
collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService<ILogger<DalamudUtilService>>(), collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService<ILogger<DalamudUtilService>>(),
clientState, objectTable, framework, gameGui, condition, gameData, clientState, objectTable, framework, gameGui, condition, gameData,
s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>())); s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>()));
collection.AddSingleton((s) => new DtrEntry(dtrBar, s.GetRequiredService<MareConfigService>(), collection.AddSingleton((s) => new DtrEntry(s.GetRequiredService<ILogger<DtrEntry>>(), dtrBar, s.GetRequiredService<MareConfigService>(),
s.GetRequiredService<PairManager>(), s.GetRequiredService<ApiController>())); s.GetRequiredService<PairManager>(), s.GetRequiredService<ApiController>()));
collection.AddSingleton((s) => new IpcManager(s.GetRequiredService<ILogger<IpcManager>>(), collection.AddSingleton((s) => new IpcManager(s.GetRequiredService<ILogger<IpcManager>>(),
pluginInterface, s.GetRequiredService<DalamudUtilService>(), s.GetRequiredService<MareMediator>())); pluginInterface, s.GetRequiredService<DalamudUtilService>(), s.GetRequiredService<MareMediator>()));
@@ -116,7 +117,8 @@ public sealed class Plugin : IDalamudPlugin
s.GetRequiredService<WindowSystem>(), s.GetServices<WindowMediatorSubscriberBase>(), s.GetRequiredService<Func<Pair, StandaloneProfileUi>>(), s.GetRequiredService<WindowSystem>(), s.GetServices<WindowMediatorSubscriberBase>(), s.GetRequiredService<Func<Pair, StandaloneProfileUi>>(),
s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareMediator>())); s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareMediator>()));
collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService<PerformanceCollectorService>(), s.GetRequiredService<UiService>(), collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService<PerformanceCollectorService>(), s.GetRequiredService<UiService>(),
s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<PeriodicFileScanner>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<MareMediator>())); s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<PeriodicFileScanner>(), s.GetRequiredService<ApiController>(),
s.GetRequiredService<MareMediator>(), s.GetRequiredService<CharacterAnalyzer>()));
collection.AddScoped((s) => new NotificationService(s.GetRequiredService<ILogger<NotificationService>>(), collection.AddScoped((s) => new NotificationService(s.GetRequiredService<ILogger<NotificationService>>(),
s.GetRequiredService<MareMediator>(), pluginInterface.UiBuilder, chatGui, s.GetRequiredService<MareConfigService>())); s.GetRequiredService<MareMediator>(), pluginInterface.UiBuilder, chatGui, s.GetRequiredService<MareConfigService>()));
collection.AddScoped((s) => new UiSharedService(s.GetRequiredService<ILogger<UiSharedService>>(), s.GetRequiredService<IpcManager>(), s.GetRequiredService<ApiController>(), collection.AddScoped((s) => new UiSharedService(s.GetRequiredService<ILogger<UiSharedService>>(), s.GetRequiredService<IpcManager>(), s.GetRequiredService<ApiController>(),

View File

@@ -0,0 +1,97 @@
using MareSynchronos.API.Data;
using MareSynchronos.FileCache;
using MareSynchronos.Services.Mediator;
using MareSynchronos.UI;
using MareSynchronos.Utils;
using Microsoft.Extensions.Logging;
namespace MareSynchronos.Services;
public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
{
private readonly FileCacheManager _fileCacheManager;
private CharacterData? _lastCreatedData;
private CancellationTokenSource? _analysisCts;
public CharacterAnalyzer(ILogger<CharacterAnalyzer> logger, MareMediator mediator, FileCacheManager fileCacheManager) : base(logger, mediator)
{
Mediator.Subscribe<CharacterDataCreatedMessage>(this, (msg) =>
{
_lastCreatedData = msg.CharacterData.DeepClone();
});
_fileCacheManager = fileCacheManager;
}
public bool IsAnalysisRunning => _analysisCts != null;
public void CancelAnalyze()
{
_analysisCts?.CancelDispose();
_analysisCts = null;
}
public async Task Analyze()
{
_analysisCts = _analysisCts?.CancelRecreate() ?? new();
var cancelToken = _analysisCts.Token;
if (_lastCreatedData == null) return;
Logger.LogInformation("=== Calculating Character Analysis, this may take a while ===");
foreach (var obj in _lastCreatedData.FileReplacements)
{
Logger.LogInformation("=== File Calculation for {obj} ===", obj.Key);
Dictionary<string, List<DataEntry>> data = new(StringComparer.OrdinalIgnoreCase);
var totalFiles = obj.Value.Count(c => !string.IsNullOrEmpty(c.Hash));
var currentFile = 1;
foreach (var hash in obj.Value.Select(c => c.Hash))
{
var fileCacheEntry = _fileCacheManager.GetFileCacheByHash(hash);
if (fileCacheEntry == null) continue;
Logger.LogInformation("Computing File {x}/{y}: {hash}", currentFile, totalFiles, hash);
Logger.LogInformation(" File Path: {path}", fileCacheEntry.ResolvedFilepath);
var filePath = fileCacheEntry.ResolvedFilepath;
FileInfo fi = new(filePath);
var ext = fi.Extension;
if (!data.ContainsKey(ext)) data[ext] = new List<DataEntry>();
(_, byte[] fileLength) = await _fileCacheManager.GetCompressedFileData(hash, cancelToken).ConfigureAwait(false);
Logger.LogInformation(" Original Size: {size}, Compressed Size: {compr}",
UiSharedService.ByteToString(fi.Length), UiSharedService.ByteToString(fileLength.LongLength));
data[ext].Add(new DataEntry(fi.FullName, fi.Length, fileLength.LongLength));
currentFile++;
cancelToken.ThrowIfCancellationRequested();
}
Logger.LogInformation("=== Summary by file type for {obj} ===", obj.Key);
foreach (var entry in data)
{
Logger.LogInformation("{ext} files: {count}, size extracted: {size}, size compressed: {sizeComp}", entry.Key, entry.Value.Count,
UiSharedService.ByteToString(entry.Value.Sum(v => v.OriginalSize)), UiSharedService.ByteToString(entry.Value.Sum(v => v.CompressedSize)));
}
Logger.LogInformation("=== Total summary for {obj} ===", obj.Key);
Logger.LogInformation("Total files: {count}, size extracted: {size}, size compressed: {sizeComp}", data.Values.Sum(c => c.Count),
UiSharedService.ByteToString(data.Values.Sum(v => v.Sum(c => c.OriginalSize))), UiSharedService.ByteToString(data.Values.Sum(v => v.Sum(c => c.CompressedSize))));
Logger.LogInformation("IMPORTANT NOTES:\n\r- For Mare up- and downloads only the compressed size is relevant.\n\r- An unusually high total files count beyond 200 and up will also increase your download time to others significantly.");
}
_analysisCts.CancelDispose();
_analysisCts = null;
}
public void Dispose()
{
_analysisCts.CancelDispose();
}
private sealed record DataEntry(string filePath, long OriginalSize, long CompressedSize);
}

View File

@@ -14,6 +14,7 @@ public sealed class CommandManagerService : IDisposable
private readonly ApiController _apiController; private readonly ApiController _apiController;
private readonly CommandManager _commandManager; private readonly CommandManager _commandManager;
private readonly MareMediator _mediator; private readonly MareMediator _mediator;
private readonly CharacterAnalyzer _characterAnalyzer;
private readonly PerformanceCollectorService _performanceCollectorService; private readonly PerformanceCollectorService _performanceCollectorService;
private readonly PeriodicFileScanner _periodicFileScanner; private readonly PeriodicFileScanner _periodicFileScanner;
private readonly ServerConfigurationManager _serverConfigurationManager; private readonly ServerConfigurationManager _serverConfigurationManager;
@@ -21,7 +22,7 @@ public sealed class CommandManagerService : IDisposable
public CommandManagerService(CommandManager commandManager, PerformanceCollectorService performanceCollectorService, public CommandManagerService(CommandManager commandManager, PerformanceCollectorService performanceCollectorService,
UiService uiService, ServerConfigurationManager serverConfigurationManager, PeriodicFileScanner periodicFileScanner, UiService uiService, ServerConfigurationManager serverConfigurationManager, PeriodicFileScanner periodicFileScanner,
ApiController apiController, MareMediator mediator) ApiController apiController, MareMediator mediator, CharacterAnalyzer characterAnalyzer)
{ {
_commandManager = commandManager; _commandManager = commandManager;
_performanceCollectorService = performanceCollectorService; _performanceCollectorService = performanceCollectorService;
@@ -30,7 +31,7 @@ public sealed class CommandManagerService : IDisposable
_periodicFileScanner = periodicFileScanner; _periodicFileScanner = periodicFileScanner;
_apiController = apiController; _apiController = apiController;
_mediator = mediator; _mediator = mediator;
_characterAnalyzer = characterAnalyzer;
_commandManager.AddHandler(_commandName, new CommandInfo(OnCommand) _commandManager.AddHandler(_commandName, new CommandInfo(OnCommand)
{ {
HelpMessage = "Opens the Mare Synchronos UI" HelpMessage = "Opens the Mare Synchronos UI"
@@ -99,5 +100,16 @@ public sealed class CommandManagerService : IDisposable
{ {
_mediator.PrintSubscriberInfo(); _mediator.PrintSubscriberInfo();
} }
else if (string.Equals(splitArgs[0], "analyze", StringComparison.OrdinalIgnoreCase))
{
if (splitArgs.Length > 1 && string.Equals(splitArgs[1], "cancel", StringComparison.OrdinalIgnoreCase))
{
_characterAnalyzer.CancelAnalyze();
}
else
{
_ = _characterAnalyzer.Analyze();
}
}
} }
} }

View File

@@ -2,9 +2,9 @@
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.MareConfiguration.Configurations; using MareSynchronos.MareConfiguration.Configurations;
using MareSynchronos.PlayerData.Pairs; using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Utils;
using MareSynchronos.WebAPI; using MareSynchronos.WebAPI;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace MareSynchronos.UI; namespace MareSynchronos.UI;
@@ -12,25 +12,25 @@ public sealed class DtrEntry : IDisposable, IHostedService
{ {
private readonly ApiController _apiController; private readonly ApiController _apiController;
private readonly CancellationTokenSource _cancellationTokenSource = new(); private readonly CancellationTokenSource _cancellationTokenSource = new();
private readonly ILogger<DtrEntry> _logger;
private readonly ConfigurationServiceBase<MareConfig> _configService; private readonly ConfigurationServiceBase<MareConfig> _configService;
private readonly DtrBarEntry _entry; private readonly Lazy<DtrBarEntry> _entry;
private readonly PairManager _pairManager; private readonly PairManager _pairManager;
private Task? _runTask; private Task? _runTask;
private string? _text; private string? _text;
public DtrEntry(DtrBar dtrBar, ConfigurationServiceBase<MareConfig> configService, PairManager pairManager, ApiController apiController) public DtrEntry(ILogger<DtrEntry> logger, DtrBar dtrBar, ConfigurationServiceBase<MareConfig> configService, PairManager pairManager, ApiController apiController)
{ {
_entry = dtrBar.Get("Mare Synchronos"); _entry = new(() => dtrBar.Get("Mare Synchronos"));
_logger = logger;
_configService = configService; _configService = configService;
_pairManager = pairManager; _pairManager = pairManager;
_apiController = apiController; _apiController = apiController;
Clear();
} }
public void Dispose() public void Dispose()
{ {
_entry.Dispose(); _entry.Value.Dispose();
} }
public Task StartAsync(CancellationToken cancellationToken) public Task StartAsync(CancellationToken cancellationToken)
@@ -49,8 +49,16 @@ public sealed class DtrEntry : IDisposable, IHostedService
catch (OperationCanceledException) { } catch (OperationCanceledException) { }
finally finally
{ {
_logger.LogDebug("Disposing DtrEntry");
if (_entry.IsValueCreated)
{
Clear();
_entry.Value.Remove();
_entry.Value.Dispose();
}
_cancellationTokenSource.Dispose(); _cancellationTokenSource.Dispose();
Clear();
} }
} }
@@ -58,16 +66,17 @@ public sealed class DtrEntry : IDisposable, IHostedService
{ {
_text = null; _text = null;
_entry.Shown = false; _entry.Value.Shown = false;
_entry.Text = null; _entry.Value.Text = null;
} }
private async Task RunAsync() private async Task RunAsync()
{ {
while (!_cancellationTokenSource.IsCancellationRequested) while (!_cancellationTokenSource.IsCancellationRequested)
{ {
Update();
await Task.Delay(1000, _cancellationTokenSource.Token).ConfigureAwait(false); await Task.Delay(1000, _cancellationTokenSource.Token).ConfigureAwait(false);
Update();
} }
} }
@@ -75,16 +84,16 @@ public sealed class DtrEntry : IDisposable, IHostedService
{ {
if (!_configService.Current.EnableDtrEntry) if (!_configService.Current.EnableDtrEntry)
{ {
if (_entry.Shown) if (_entry.Value.Shown)
{ {
Clear(); Clear();
} }
return; return;
} }
if (!_entry.Shown) if (!_entry.Value.Shown)
{ {
_entry.Shown = true; _entry.Value.Shown = true;
} }
string text; string text;
@@ -99,7 +108,7 @@ public sealed class DtrEntry : IDisposable, IHostedService
if (!string.Equals(text, _text, StringComparison.Ordinal)) if (!string.Equals(text, _text, StringComparison.Ordinal))
{ {
_text = text; _text = text;
_entry.Text = text; _entry.Value.Text = text;
} }
} }
} }

View File

@@ -29,6 +29,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new(); private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
private readonly FileUploadManager _fileTransferManager; private readonly FileUploadManager _fileTransferManager;
private readonly FileTransferOrchestrator _fileTransferOrchestrator; private readonly FileTransferOrchestrator _fileTransferOrchestrator;
private readonly CharacterAnalyzer _characterAnalyzer;
private readonly MareCharaFileManager _mareCharaFileManager; private readonly MareCharaFileManager _mareCharaFileManager;
private readonly PairManager _pairManager; private readonly PairManager _pairManager;
private readonly PerformanceCollectorService _performanceCollector; private readonly PerformanceCollectorService _performanceCollector;
@@ -50,7 +51,8 @@ public class SettingsUi : WindowMediatorSubscriberBase
ServerConfigurationManager serverConfigurationManager, ServerConfigurationManager serverConfigurationManager,
MareMediator mediator, PerformanceCollectorService performanceCollector, MareMediator mediator, PerformanceCollectorService performanceCollector,
FileUploadManager fileTransferManager, FileUploadManager fileTransferManager,
FileTransferOrchestrator fileTransferOrchestrator) : base(logger, mediator, "Mare Synchronos Settings") FileTransferOrchestrator fileTransferOrchestrator,
CharacterAnalyzer characterAnalyzer) : base(logger, mediator, "Mare Synchronos Settings")
{ {
_configService = configService; _configService = configService;
_mareCharaFileManager = mareCharaFileManager; _mareCharaFileManager = mareCharaFileManager;
@@ -59,6 +61,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
_performanceCollector = performanceCollector; _performanceCollector = performanceCollector;
_fileTransferManager = fileTransferManager; _fileTransferManager = fileTransferManager;
_fileTransferOrchestrator = fileTransferOrchestrator; _fileTransferOrchestrator = fileTransferOrchestrator;
_characterAnalyzer = characterAnalyzer;
_uiShared = uiShared; _uiShared = uiShared;
SizeConstraints = new WindowSizeConstraints() SizeConstraints = new WindowSizeConstraints()
@@ -331,6 +334,23 @@ public class SettingsUi : WindowMediatorSubscriberBase
} }
UiSharedService.AttachToolTip("Use this when reporting mods being rejected from the server."); UiSharedService.AttachToolTip("Use this when reporting mods being rejected from the server.");
var isAnalyzing = _characterAnalyzer.IsAnalysisRunning;
if (isAnalyzing) ImGui.BeginDisabled();
if (UiSharedService.IconTextButton(FontAwesomeIcon.QuestionCircle, "[DEBUG] Analyze current character composition to /xllog"))
{
_ = _characterAnalyzer.Analyze();
}
UiSharedService.AttachToolTip("This will compute your current \"Mare load\" and print it to the /xllog");
if (isAnalyzing) ImGui.EndDisabled();
ImGui.SameLine();
if (!isAnalyzing) ImGui.BeginDisabled();
if (UiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Cancel analysis"))
{
_characterAnalyzer.CancelAnalyze();
}
UiSharedService.AttachToolTip("Cancels the current analysis of your character composition");
if (!isAnalyzing) ImGui.EndDisabled();
_uiShared.DrawCombo("Log Level", Enum.GetValues<LogLevel>(), (l) => l.ToString(), (l) => _uiShared.DrawCombo("Log Level", Enum.GetValues<LogLevel>(), (l) => l.ToString(), (l) =>
{ {
_configService.Current.LogLevel = l; _configService.Current.LogLevel = l;

View File

@@ -106,13 +106,6 @@ public sealed class FileUploadManager : DisposableMediatorSubscriberBase
return await response.Content.ReadFromJsonAsync<List<UploadFileDto>>(cancellationToken: ct).ConfigureAwait(false) ?? new List<UploadFileDto>(); return await response.Content.ReadFromJsonAsync<List<UploadFileDto>>(cancellationToken: ct).ConfigureAwait(false) ?? new List<UploadFileDto>();
} }
private async Task<(string, byte[])> GetCompressedFileData(string fileHash, CancellationToken uploadToken)
{
var fileCache = _fileDbManager.GetFileCacheByHash(fileHash)!.ResolvedFilepath;
return (fileHash, LZ4Codec.WrapHC(await File.ReadAllBytesAsync(fileCache, uploadToken).ConfigureAwait(false), 0,
(int)new FileInfo(fileCache).Length));
}
private HashSet<string> GetUnverifiedFiles(CharacterData data) private HashSet<string> GetUnverifiedFiles(CharacterData data)
{ {
HashSet<string> unverifiedUploadHashes = new(StringComparer.Ordinal); HashSet<string> unverifiedUploadHashes = new(StringComparer.Ordinal);
@@ -245,7 +238,7 @@ public sealed class FileUploadManager : DisposableMediatorSubscriberBase
foreach (var file in CurrentUploads.Where(f => f.CanBeTransferred && !f.IsTransferred).ToList()) foreach (var file in CurrentUploads.Where(f => f.CanBeTransferred && !f.IsTransferred).ToList())
{ {
Logger.LogDebug("[{hash}] Compressing", file); Logger.LogDebug("[{hash}] Compressing", file);
var data = await GetCompressedFileData(file.Hash, uploadToken).ConfigureAwait(false); var data = await _fileDbManager.GetCompressedFileData(file.Hash, uploadToken).ConfigureAwait(false);
CurrentUploads.Single(e => string.Equals(e.Hash, data.Item1, StringComparison.Ordinal)).Total = data.Item2.Length; CurrentUploads.Single(e => string.Equals(e.Hash, data.Item1, StringComparison.Ordinal)).Total = data.Item2.Length;
Logger.LogDebug("[{hash}] Starting upload for {filePath}", data.Item1, _fileDbManager.GetFileCacheByHash(data.Item1)!.ResolvedFilepath); Logger.LogDebug("[{hash}] Starting upload for {filePath}", data.Item1, _fileDbManager.GetFileCacheByHash(data.Item1)!.ResolvedFilepath);
await uploadTask.ConfigureAwait(false); await uploadTask.ConfigureAwait(false);