diff --git a/src/Artemis.ConsoleUI/Program.cs b/src/Artemis.ConsoleUI/Program.cs
index 0526b6fce..6d694d8d6 100644
--- a/src/Artemis.ConsoleUI/Program.cs
+++ b/src/Artemis.ConsoleUI/Program.cs
@@ -4,6 +4,7 @@ using System.Threading;
using Artemis.Core;
using Artemis.Core.Ninject;
using Artemis.Core.Services;
+using Artemis.Storage;
using Ninject;
namespace Artemis.UI.Console
@@ -28,6 +29,8 @@ namespace Artemis.UI.Console
private static void Main(string[] args)
{
+ StorageManager.CreateBackup(Constants.DataFolder);
+
Utilities.PrepareFirstLaunch();
Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
StandardKernel kernel = new() {Settings = {InjectNonPublic = true}};
diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs
index 6f8f1c27d..e978e8598 100644
--- a/src/Artemis.Core/Constants.cs
+++ b/src/Artemis.Core/Constants.cs
@@ -28,12 +28,7 @@ namespace Artemis.Core
/// The full path to the Artemis data folder
///
public static readonly string DataFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\Artemis\\";
-
- ///
- /// The connection string used to connect to the database
- ///
- public static readonly string ConnectionString = $"FileName={DataFolder}\\database.db";
-
+
///
/// The plugin info used by core components of Artemis
///
diff --git a/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs b/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs
index 2df270e5b..e2f51547d 100644
--- a/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs
+++ b/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs
@@ -102,6 +102,26 @@ namespace Artemis.Core
foreach (LedId led in ledsToRemove)
device.RemoveLed(led);
}
+
+ List deviceLeds = device.ToList();
+ foreach (Led led in deviceLeds)
+ {
+ float x = led.Location.X;
+ float y = led.Location.Y;
+
+ // Try to move the LED if it falls outside the boundaries of the layout
+ if (led.Location.X + led.Size.Width > device.Size.Width)
+ x -= led.Location.X + led.Size.Width - device.Size.Width;
+
+ if (led.Location.Y + led.Size.Height > device.Size.Height)
+ y -= led.Location.Y + led.Size.Height - device.Size.Height;
+
+ // If not possible because it's too large we'll have to drop it to avoid rendering issues
+ if (x < 0 || y < 0)
+ device.RemoveLed(led.Id);
+ else
+ led.Location = new Point(x, y);
+ }
}
internal void ApplyDevice(ArtemisDevice artemisDevice)
diff --git a/src/Artemis.Core/Ninject/CoreModule.cs b/src/Artemis.Core/Ninject/CoreModule.cs
index b3dee7af1..66a882bf7 100644
--- a/src/Artemis.Core/Ninject/CoreModule.cs
+++ b/src/Artemis.Core/Ninject/CoreModule.cs
@@ -50,24 +50,7 @@ namespace Artemis.Core.Ninject
.Configure(c => c.When(HasAccessToProtectedService).InSingletonScope());
});
- Kernel.Bind().ToMethod(t =>
- {
- try
- {
- return new LiteRepository(Constants.ConnectionString);
- }
- catch (LiteException e)
- {
- // I don't like this way of error reporting, now I need to use reflection if I want a meaningful error code
- if (e.ErrorCode != LiteException.INVALID_DATABASE)
- throw new ArtemisCoreException($"LiteDB threw error code {e.ErrorCode}. See inner exception for more details", e);
-
- // If the DB is invalid it's probably LiteDB v4 (TODO: we'll have to do something better later)
- File.Delete($"{Constants.DataFolder}\\database.db");
- return new LiteRepository(Constants.ConnectionString);
- }
- }).InSingletonScope();
-
+ Kernel.Bind().ToMethod(_ => StorageManager.CreateRepository(Constants.DataFolder)).InSingletonScope();
Kernel.Bind().ToSelf().InSingletonScope();
// Bind all migrations as singletons
diff --git a/src/Artemis.Storage/StorageManager.cs b/src/Artemis.Storage/StorageManager.cs
new file mode 100644
index 000000000..267f86fea
--- /dev/null
+++ b/src/Artemis.Storage/StorageManager.cs
@@ -0,0 +1,63 @@
+using System;
+using System.IO;
+using System.Linq;
+using LiteDB;
+
+namespace Artemis.Storage
+{
+ public static class StorageManager
+ {
+ private static bool _inUse;
+
+ ///
+ /// Creates a backup of the database if the last backup is older than 10 minutes
+ /// Removes the oldest backup if there are more than 5 backups present
+ ///
+ /// The Artemis data folder
+ public static void CreateBackup(string dataFolder)
+ {
+ if (_inUse)
+ throw new Exception("Storage is already in use, can't backup now.");
+
+ string database = $"{dataFolder}\\database.db";
+ if (!File.Exists(database))
+ return;
+
+ string backupFolder = $"{dataFolder}\\database backups";
+ Directory.CreateDirectory(backupFolder);
+ FileSystemInfo[] files = new DirectoryInfo(backupFolder).GetFileSystemInfos();
+ if (files.Length >= 5)
+ {
+ FileSystemInfo newest = files.OrderByDescending(fi => fi.CreationTime).First();
+ FileSystemInfo oldest = files.OrderBy(fi => fi.CreationTime).First();
+ if (DateTime.Now - newest.CreationTime < TimeSpan.FromMinutes(10))
+ return;
+
+ oldest.Delete();
+ }
+
+ File.Copy(database, $"{backupFolder}\\database-{DateTime.Now:yyyy-dd-M--HH-mm-ss}.db");
+ }
+
+ ///
+ /// Creates the LiteRepository that will be managed by dependency injection
+ ///
+ /// The Artemis data folder
+ public static LiteRepository CreateRepository(string dataFolder)
+ {
+ if (_inUse)
+ throw new Exception("Storage is already in use, use dependency injection to get the repository.");
+
+ try
+ {
+ _inUse = true;
+ return new LiteRepository($"FileName={dataFolder}\\database.db");
+ }
+ catch (LiteException e)
+ {
+ // I don't like this way of error reporting, now I need to use reflection if I want a meaningful error message
+ throw new Exception($"LiteDB threw error code {e.ErrorCode}. See inner exception for more details", e);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Bootstrapper.cs b/src/Artemis.UI/Bootstrapper.cs
index 6b27e2fd1..d444e16e2 100644
--- a/src/Artemis.UI/Bootstrapper.cs
+++ b/src/Artemis.UI/Bootstrapper.cs
@@ -6,8 +6,10 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Threading;
+using Artemis.Core;
using Artemis.Core.Ninject;
using Artemis.Core.Services;
+using Artemis.Storage;
using Artemis.UI.Ninject;
using Artemis.UI.Screens;
using Artemis.UI.Services;
@@ -25,6 +27,13 @@ namespace Artemis.UI
{
private ApplicationStateManager _applicationStateManager;
private ICoreService _core;
+
+ public Bootstrapper()
+ {
+ // This is done at this kind of odd place to ensure it happens before the database is in use
+ StorageManager.CreateBackup(Constants.DataFolder);
+ }
+
public static List StartupArguments { get; private set; }
protected override void OnExit(ExitEventArgs e)