From a90958a7bf114ca9606cafe3c1bd9f1b43a5dfab Mon Sep 17 00:00:00 2001 From: "Herr, Dominik" Date: Tue, 18 Jun 2024 10:35:12 +0200 Subject: [PATCH] SKA-224: Delete temporarily extracted FMUs * If there is a folder with the extracted FMU in the same directory as the FMU, this extracted content will be used and remains there after the FMU Importer stops. * Otherwise, the FMU is now extracted to a temporary directory. This directory and its contents are deleted after the simulation run. --- CHANGELOG.md | 3 + .../FmiBridge/Binding/BindingFactory.cs | 7 +- FmuImporter/FmiBridge/Binding/Fmi2Binding.cs | 2 +- FmuImporter/FmiBridge/Binding/Fmi3Binding.cs | 2 +- .../FmiBridge/Binding/FmiBindingBase.cs | 67 ++++++++++++++++--- .../FmiBridge/Binding/IFmiBindingCommon.cs | 2 - FmuImporter/FmiBridge/FmiModel/ModelLoader.cs | 27 +++++--- FmuImporter/FmuImporter/Fmu/FmuEntity.cs | 6 +- FmuImporter/FmuImporter/FmuImporter.cs | 5 +- 9 files changed, 90 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9e11dc..931bdaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ The format is based on `Keep a Changelog (http://keepachangelog.com/en/1.0.0/) < ### Changed * If the value of an enumeration-typed variable is set, strings are now considered referring to an enumerator's name, whereas integers refer to an enumerators' value. +* Temporarily extracted FMUs are now deleted by default + * If there is a folder with the extracted FMU in the same directory as the FMU, this extracted content will be used and remains there after the FMU Importer stops. + * Otherwise, the FMU is now extracted to a temporary directory. This directory and its contents are deleted after the simulation run. ## [1.2.0] - 2024-03-28 diff --git a/FmuImporter/FmiBridge/Binding/BindingFactory.cs b/FmuImporter/FmiBridge/Binding/BindingFactory.cs index 126c7e7..8cb11e0 100644 --- a/FmuImporter/FmiBridge/Binding/BindingFactory.cs +++ b/FmuImporter/FmiBridge/Binding/BindingFactory.cs @@ -5,14 +5,15 @@ namespace Fmi.Binding; public static class BindingFactory { - public static IFmiBindingCommon CreateBinding(FmiVersions fmiVersion, string fmuPath) + public static IFmiBindingCommon CreateBinding( + FmiVersions fmiVersion, string fmuPath, Action logCallback) { switch (fmiVersion) { case FmiVersions.Fmi2: - return new Fmi2Binding(fmuPath); + return new Fmi2Binding(fmuPath, logCallback); case FmiVersions.Fmi3: - return new Fmi3Binding(fmuPath); + return new Fmi3Binding(fmuPath, logCallback); default: throw new ArgumentOutOfRangeException(nameof(fmiVersion), fmiVersion, null); } diff --git a/FmuImporter/FmiBridge/Binding/Fmi2Binding.cs b/FmuImporter/FmiBridge/Binding/Fmi2Binding.cs index b7b59c2..3f643bd 100644 --- a/FmuImporter/FmiBridge/Binding/Fmi2Binding.cs +++ b/FmuImporter/FmiBridge/Binding/Fmi2Binding.cs @@ -114,7 +114,7 @@ private static string OsPath private static readonly AutoResetEvent WaitForDoStepEvent = new AutoResetEvent(false); - public Fmi2Binding(string fmuPath) : base(fmuPath, OsPath) + public Fmi2Binding(string fmuPath, Action logCallback) : base(fmuPath, OsPath, logCallback) { // Common Functions SetDelegate(out _fmi2SetDebugLogging); diff --git a/FmuImporter/FmiBridge/Binding/Fmi3Binding.cs b/FmuImporter/FmiBridge/Binding/Fmi3Binding.cs index 57112f2..ec7f87e 100644 --- a/FmuImporter/FmiBridge/Binding/Fmi3Binding.cs +++ b/FmuImporter/FmiBridge/Binding/Fmi3Binding.cs @@ -48,7 +48,7 @@ private static string OsPath } } - public Fmi3Binding(string fmuPath) : base(fmuPath, OsPath) + public Fmi3Binding(string fmuPath, Action logCallback) : base(fmuPath, OsPath, logCallback) { // Common functions SetDelegate(out _fmi3InstantiateCoSimulation); diff --git a/FmuImporter/FmiBridge/Binding/FmiBindingBase.cs b/FmuImporter/FmiBridge/Binding/FmiBindingBase.cs index 0b3ef33..0e4a0a9 100644 --- a/FmuImporter/FmiBridge/Binding/FmiBindingBase.cs +++ b/FmuImporter/FmiBridge/Binding/FmiBindingBase.cs @@ -23,15 +23,43 @@ internal abstract class FmiBindingBase : IDisposable, IFmiBindingCommon public ModelDescription ModelDescription { get; } - public string ExtractedFolderPath { get; } + private readonly string _extractedFolderPath; + + public string ExtractedFolderPath + { + get + { + return _extractedFolderPath; + } + } + public string FullFmuLibPath { get; } private IntPtr DllPtr { set; get; } + private bool _isTemporary; + + internal bool IsTemporary + { + get + { + return _isTemporary; + } + set + { + _isTemporary = value; + } + } + // ctor - protected FmiBindingBase(string fmuPath, string osDependentPath) + protected FmiBindingBase(string fmuPath, string osDependentPath, Action logCallback) { - ExtractedFolderPath = ModelLoader.ExtractFmu(fmuPath); + _loggerAction = logCallback; + ModelLoader.ExtractFmu(fmuPath, out _extractedFolderPath, out _isTemporary); + if (IsTemporary) + { + Log(LogSeverity.Debug, $"Temporarily extracted the FMU to '{_extractedFolderPath}'."); + } ModelDescription = InitializeModelDescription(ExtractedFolderPath); @@ -49,6 +77,32 @@ protected FmiBindingBase(string fmuPath, string osDependentPath) private void ReleaseUnmanagedResources() { + var failCounter = 10; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + while (NativeMethods.FreeLibrary(DllPtr) && failCounter > 0) + { + --failCounter; + } + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + while (NativeMethods.dlclose(DllPtr) != 0 && failCounter > 0) + { + --failCounter; + } + } + else + { + throw new NotSupportedException(); + } + + // if DLL was freed successfully and FMU was extracted to temporary folder, delete that folder and its content + if (failCounter > 0 && IsTemporary) + { + var dir = Directory.GetParent(ExtractedFolderPath) ?? new DirectoryInfo(ExtractedFolderPath); + dir.Delete(true); + } } private bool _disposedValue; @@ -235,10 +289,5 @@ protected void Log(LogSeverity severity, string message) _loggerAction?.Invoke(severity, message); } - private Action? _loggerAction; - - public void SetLoggerCallback(Action callback) - { - _loggerAction = callback; - } + private readonly Action? _loggerAction; } diff --git a/FmuImporter/FmiBridge/Binding/IFmiBindingCommon.cs b/FmuImporter/FmiBridge/Binding/IFmiBindingCommon.cs index 2627c73..3951dba 100644 --- a/FmuImporter/FmiBridge/Binding/IFmiBindingCommon.cs +++ b/FmuImporter/FmiBridge/Binding/IFmiBindingCommon.cs @@ -26,6 +26,4 @@ public void DoStep( public void FreeInstance(); public FmiVersions GetFmiVersion(); - - public void SetLoggerCallback(Action callback); } diff --git a/FmuImporter/FmiBridge/FmiModel/ModelLoader.cs b/FmuImporter/FmiBridge/FmiModel/ModelLoader.cs index 1bf391a..388acba 100644 --- a/FmuImporter/FmiBridge/FmiModel/ModelLoader.cs +++ b/FmuImporter/FmiBridge/FmiModel/ModelLoader.cs @@ -10,7 +10,7 @@ namespace Fmi.FmiModel; public class ModelLoader { - internal static string ExtractFmu(string fmuPath) + internal static void ExtractFmu(string fmuPath, out string extractedPath, out bool isTemporary) { // check if the directory exists var targetFolderPath = @@ -21,23 +21,34 @@ internal static string ExtractFmu(string fmuPath) // $"SilKitImporter_{Path.GetFileNameWithoutExtension(fmuPath)}"); if (Directory.Exists(targetFolderPath) && Directory.EnumerateFileSystemEntries(targetFolderPath).Any()) { + isTemporary = false; + extractedPath = targetFolderPath; // directory exists and has entries -> skip extraction - return targetFolderPath; + return; } + var tempDirectory = CreateTempDirectory(); + var dir = Directory.CreateDirectory( - $"{Path.GetDirectoryName(fmuPath)}/{Path.GetFileNameWithoutExtension(fmuPath)}"); + $"{tempDirectory}/{Path.GetFileNameWithoutExtension(fmuPath)}"); ZipFile.ExtractToDirectory(fmuPath, dir.FullName); - return dir.FullName; + isTemporary = true; + extractedPath = dir.FullName; } - internal static void RemoveExtractedFmu(string fmuPath) + private static string CreateTempDirectory() { - var dir = $"{Path.GetDirectoryName(fmuPath)}/{Path.GetFileNameWithoutExtension(fmuPath)}"; - if (Directory.Exists(dir)) + var tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + if (File.Exists(tempDirectory)) + { + // try again + return CreateTempDirectory(); + } + else { - Directory.Delete(dir, true); + Directory.CreateDirectory(tempDirectory); + return tempDirectory; } } diff --git a/FmuImporter/FmuImporter/Fmu/FmuEntity.cs b/FmuImporter/FmuImporter/Fmu/FmuEntity.cs index 8d871ef..323f7e3 100644 --- a/FmuImporter/FmuImporter/Fmu/FmuEntity.cs +++ b/FmuImporter/FmuImporter/Fmu/FmuEntity.cs @@ -29,12 +29,12 @@ public enum FmuSuperStates public event FmiLog? OnFmuLog; - public FmuEntity(string fmuPath) + public FmuEntity(string fmuPath, FmiLog fmiLogCallback) { FmiVersion = ModelLoader.FindFmiVersion(fmuPath); + OnFmuLog = fmiLogCallback; - Binding = BindingFactory.CreateBinding(FmiVersion, fmuPath); - Binding.SetLoggerCallback(RaiseOnFmuLogEvent); + Binding = BindingFactory.CreateBinding(FmiVersion, fmuPath, RaiseOnFmuLogEvent); ModelDescription = Binding.ModelDescription; } diff --git a/FmuImporter/FmuImporter/FmuImporter.cs b/FmuImporter/FmuImporter/FmuImporter.cs index 0fe242b..51a3e04 100644 --- a/FmuImporter/FmuImporter/FmuImporter.cs +++ b/FmuImporter/FmuImporter/FmuImporter.cs @@ -98,7 +98,7 @@ public FmuImporter( throw; } - FmuEntity = new FmuEntity(fmuPath); + FmuEntity = new FmuEntity(fmuPath, FmuEntity_OnFmuLog); try { @@ -119,9 +119,6 @@ public FmuImporter( _configuredStructuralParameters.Add(myStructParam, param!); } - // Register logger callback - FmuEntity.OnFmuLog += FmuEntity_OnFmuLog; - // Initialize FMU FmuEntity.PrepareFmu(ApplyParameterConfiguration, ApplyParameterConfiguration);