diff --git a/csharp/App/Backend/Backend.csproj b/csharp/App/Backend/Backend.csproj
index 6959e506d..bf230bd4e 100644
--- a/csharp/App/Backend/Backend.csproj
+++ b/csharp/App/Backend/Backend.csproj
@@ -27,6 +27,7 @@
+
diff --git a/csharp/App/Backend/Controller.cs b/csharp/App/Backend/Controller.cs
index f3e80527e..74b45cb9a 100644
--- a/csharp/App/Backend/Controller.cs
+++ b/csharp/App/Backend/Controller.cs
@@ -20,7 +20,6 @@ public class Controller : ControllerBase
if (user is null)
{
- Console.WriteLine("I have no user");
throw new Exceptions(400,"Null User Exception", "Must provide a user to log in as.", Request.Path.Value!);
}
diff --git a/csharp/App/Backend/DataTypes/Methods/Session.cs b/csharp/App/Backend/DataTypes/Methods/Session.cs
index 4856816cd..2c259ffc9 100644
--- a/csharp/App/Backend/DataTypes/Methods/Session.cs
+++ b/csharp/App/Backend/DataTypes/Methods/Session.cs
@@ -55,10 +55,9 @@ public static class SessionMethods
var installation = Db.GetInstallationById(installationId);
var parent = Db.GetFolderById(parentId);
- if(installation.ParentId == parentId) return false;
+ if(installation == null || installation.ParentId == parentId) return false;
return user is not null
- && installation is not null
&& user.HasWriteAccess
&& user.HasAccessTo(installation)
&& user.HasAccessTo(parent)
diff --git a/csharp/App/Backend/Database/Create.cs b/csharp/App/Backend/Database/Create.cs
index 95849acaa..24944ba2b 100644
--- a/csharp/App/Backend/Database/Create.cs
+++ b/csharp/App/Backend/Database/Create.cs
@@ -7,54 +7,61 @@ namespace InnovEnergy.App.Backend.Database;
public static partial class Db
{
+ private static Boolean Insert(Object obj)
+ {
+ var success = Connection.Insert(obj) > 0;
+ if(success) BackupDatabase();
+ return success;
+ }
+
public static Boolean Create(Installation installation)
{
// SQLite wrapper is smart and *modifies* t's Id to the one generated (autoincrement) by the insertion
- return Connection.Insert(installation) > 0;
+ return Insert(installation);
}
public static Boolean Create(DeletedInstallation installation)
{
- return Connection.Insert(installation) > 0;
+ return Insert(installation);
}
public static Boolean Create(Folder folder)
{
- return Connection.Insert(folder) > 0;
+ return Insert(folder);
}
public static Boolean Create(DeletedFolder folder)
{
- return Connection.Insert(folder) > 0;
+ return Insert(folder);
}
public static Boolean Create(User user)
{
- return Connection.Insert(user) > 0;
+ return Insert(user);
}
public static Boolean Create(DeletedUser user)
{
- return Connection.Insert(user) > 0;
+ return Insert(user);
}
public static Boolean Create(Session session)
{
- return Connection.Insert(session) > 0;
+ return Insert(session);
}
public static Boolean Create(InstallationAccess installationAccess)
{
- return Connection.Insert(installationAccess) > 0;
+ return Insert(installationAccess);
}
public static Boolean Create(FolderAccess folderAccess)
{
- return Connection.Insert(folderAccess) > 0;
+ return Insert(folderAccess);
}
public static Boolean Create(OrderNumber2Installation o2i)
{
- return Connection.Insert(o2i) > 0;
+ return Insert(o2i);
}
}
\ No newline at end of file
diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs
index ea82f6788..a3918d2cb 100644
--- a/csharp/App/Backend/Database/Db.cs
+++ b/csharp/App/Backend/Database/Db.cs
@@ -1,12 +1,16 @@
+using System.Data.SQLite;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
+using System.Runtime.InteropServices;
using CliWrap;
using CliWrap.Buffered;
using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.App.Backend.DataTypes.Methods;
using InnovEnergy.App.Backend.Relations;
using InnovEnergy.Lib.Utils;
+using Microsoft.Identity.Client;
using SQLite;
+using SQLiteConnection = SQLite.SQLiteConnection;
namespace InnovEnergy.App.Backend.Database;
@@ -16,8 +20,79 @@ public static partial class Db
{
internal const String DbPath = "./db.sqlite";
- private static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath);
+ private static SQLiteConnection Connection { get; } = ((Func)(() =>
+ {
+ var latestDb = new DirectoryInfo(@"DbBackups").GetFiles()
+ .OrderBy(f => f.LastWriteTime)
+ .First().Name;
+ var fileConnection = new SQLiteConnection("DbBackups/"+latestDb);
+
+ var memoryConnection = new SQLiteConnection(":memory:");
+
+ // fileConnection.Backup(memoryConnection.DatabasePath);
+
+ memoryConnection.CreateTable();
+ memoryConnection.CreateTable();
+ memoryConnection.CreateTable();
+ memoryConnection.CreateTable();
+ memoryConnection.CreateTable();
+ memoryConnection.CreateTable();
+ memoryConnection.CreateTable();
+ memoryConnection.CreateTable();
+ memoryConnection.CreateTable();
+ memoryConnection.CreateTable();
+
+ foreach (var obj in fileConnection.Table())
+ {
+ memoryConnection.Insert(obj);
+ }
+ foreach (var obj in fileConnection.Table())
+ {
+ memoryConnection.Insert(obj);
+ }
+ foreach (var obj in fileConnection.Table())
+ {
+ memoryConnection.Insert(obj);
+ }
+ foreach (var obj in fileConnection.Table())
+ {
+ memoryConnection.Insert(obj);
+ }
+ foreach (var obj in fileConnection.Table())
+ {
+ memoryConnection.Insert(obj);
+ }
+ foreach (var obj in fileConnection.Table())
+ {
+ memoryConnection.Insert(obj);
+ }
+ foreach (var obj in fileConnection.Table())
+ {
+ memoryConnection.Insert(obj);
+ }
+ foreach (var obj in fileConnection.Table())
+ {
+ memoryConnection.Insert(obj);
+ }
+ foreach (var obj in fileConnection.Table())
+ {
+ memoryConnection.Insert(obj);
+ }
+ foreach (var obj in fileConnection.Table())
+ {
+ memoryConnection.Insert(obj);
+ }
+
+ return memoryConnection;
+ }))();
+
+ public static void BackupDatabase()
+ {
+ var filename = "db-" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ".sqlite";
+ Connection.Backup("DbBackups/"+filename);
+ }
+
public static TableQuery Sessions => Connection.Table();
public static TableQuery Folders => Connection.Table();
public static TableQuery Installations => Connection.Table();
diff --git a/csharp/App/Backend/Database/Delete.cs b/csharp/App/Backend/Database/Delete.cs
index 200b63855..a99229584 100644
--- a/csharp/App/Backend/Database/Delete.cs
+++ b/csharp/App/Backend/Database/Delete.cs
@@ -22,9 +22,10 @@ public static partial class Db
Boolean DeleteDescendantFolderAndItsDependencies(Folder f)
{
FolderAccess .Delete(r => r.FolderId == f.Id);
- Installations.Delete(r => r.ParentId == f.Id);
-
- return Folders.Delete(r => r.Id == f.Id) > 0;
+ Installations.Delete(r => r.ParentId == f.Id);
+ var delete = Folders.Delete(r => r.Id == f.Id);
+ BackupDatabase();
+ return delete > 0;
}
}
@@ -35,7 +36,9 @@ public static partial class Db
Boolean DeleteInstallationAndItsDependencies()
{
InstallationAccess.Delete(i => i.InstallationId == installation.Id);
- return Installations.Delete(i => i.Id == installation.Id) > 0;
+ var delete = Installations.Delete(i => i.Id == installation.Id);
+ BackupDatabase();
+ return delete > 0;
}
}
@@ -48,7 +51,9 @@ public static partial class Db
FolderAccess .Delete(u => u.UserId == user.Id);
InstallationAccess.Delete(u => u.UserId == user.Id);
- return Users.Delete(u => u.Id == user.Id) > 0;
+ var delete = Users.Delete(u => u.Id == user.Id);
+ BackupDatabase();
+ return delete > 0;
}
}
@@ -58,6 +63,8 @@ public static partial class Db
// private!!
private static Boolean Delete(Session session)
{
- return Sessions.Delete(s => s.Id == session.Id) > 0;
+ var delete = Sessions.Delete(s => s.Id == session.Id) > 0;
+ BackupDatabase();
+ return delete;
}
}
\ No newline at end of file
diff --git a/csharp/App/Backend/Database/Update.cs b/csharp/App/Backend/Database/Update.cs
index a90749a45..094487552 100644
--- a/csharp/App/Backend/Database/Update.cs
+++ b/csharp/App/Backend/Database/Update.cs
@@ -5,14 +5,21 @@ namespace InnovEnergy.App.Backend.Database;
public static partial class Db
{
+ private static Boolean Update(Object obj)
+ {
+ var success = Connection.Update(obj) > 0;
+ if(success) BackupDatabase();
+ return success;
+ }
+
public static Boolean Update(Folder folder)
{
- return Connection.Update(folder) > 0;
+ return Update(obj: folder);
}
public static Boolean Update(Installation installation)
{
- return Connection.Update(installation) > 0;
+ return Update(obj: installation);
}
public static Boolean Update(User user)
@@ -24,7 +31,7 @@ public static partial class Db
user.ParentId = originalUser.ParentId;
user.Name = originalUser.Name;
- return Connection.Update(user) > 0;
+ return Update(obj: user);
}
diff --git a/csharp/App/Backend/db.sqlite b/csharp/App/Backend/DbBackups/db.sqlite
similarity index 99%
rename from csharp/App/Backend/db.sqlite
rename to csharp/App/Backend/DbBackups/db.sqlite
index 9a363a376..55d2bbbeb 100644
Binary files a/csharp/App/Backend/db.sqlite and b/csharp/App/Backend/DbBackups/db.sqlite differ
diff --git a/csharp/App/Backend/Resources/s3AdminKey.json b/csharp/App/Backend/Resources/s3AdminKey.json
new file mode 100644
index 000000000..0cacecb5b
--- /dev/null
+++ b/csharp/App/Backend/Resources/s3AdminKey.json
@@ -0,0 +1,4 @@
+{
+ "Key": "EXO1abcb772bf43ab72951ba1dc",
+ "Secret": "_ym1KsGBSp90S5dwhZn18XD-u9Y4ghHvyIxg5gv5fHw"
+}
\ No newline at end of file
diff --git a/csharp/App/Backend/S3/S3Access.cs b/csharp/App/Backend/S3/S3Access.cs
index d509ae460..03dcfb4c7 100644
--- a/csharp/App/Backend/S3/S3Access.cs
+++ b/csharp/App/Backend/S3/S3Access.cs
@@ -9,9 +9,5 @@ public static class S3Access
{
public static S3Cmd ReadOnly => Deserialize(OpenRead("./Resources/s3ReadOnlyKey.json"))!;
public static S3Cmd ReadWrite => Deserialize(OpenRead("./Resources/s3ReadWriteKey.json"))!;
-
- public static async Task CreateKey(String bucketName)
- {
- throw new NotImplementedException();
- }
+ public static S3Cmd Admin => Deserialize(OpenRead("./Resources/s3AdminKey.json"))!;
}
\ No newline at end of file
diff --git a/csharp/App/Backend/S3/S3Cmd.cs b/csharp/App/Backend/S3/S3Cmd.cs
index 6cd4a8f6e..40c32fa09 100644
--- a/csharp/App/Backend/S3/S3Cmd.cs
+++ b/csharp/App/Backend/S3/S3Cmd.cs
@@ -57,11 +57,23 @@ public class S3Cmd
";
var result = await Run(bucketName, "mb");
- var setCors = await Run(bucketName, "PutBucketCors", cors);
+ var setCors = await Run(bucketName, "setcors", cors);
return result.ExitCode == 0 && setCors.ExitCode == 0;
}
+ public async Task ListFilesInBucket(String bucketName)
+ {
+ var result = await Run(bucketName, "ls");
+ return result.StandardOutput;
+ }
+
+ public async Task GetFileText(String bucketName, String filename)
+ {
+ var result = await Run(bucketName + "/" + filename, "get", "--force");
+ return File.ReadAllLines("./" + filename);
+ }
+
public async Task DeleteBucket(String bucketName)
{
var result = await Run(bucketName, "rb");
@@ -86,4 +98,5 @@ public class S3Cmd
.WithArguments(args)
.ExecuteBufferedAsync();
}
+
}
\ No newline at end of file
diff --git a/csharp/App/S3Explorer/Program.cs b/csharp/App/S3Explorer/Program.cs
new file mode 100644
index 000000000..69c9d8890
--- /dev/null
+++ b/csharp/App/S3Explorer/Program.cs
@@ -0,0 +1,94 @@
+using InnovEnergy.App.Backend.S3;
+using InnovEnergy.Lib.Utils;
+
+namespace S3Explorer;
+
+public static class Program
+{
+
+ public static async Task Main(String[] args)
+ {
+ // Todo refactor S3Access into Lib
+
+ // Sssssecret
+ if (args.Contains("-s"))
+ {
+ await SnakeGameSs.PlaySnake();
+ }
+
+ // Help message
+ if (args.Length < 4 || args.Contains("-h"))
+ {
+ Console.WriteLine("Usage: S3Explorer [BucketId] [from:Unix-time] [to:Unix-time] [#Data-points]");
+ Console.WriteLine("-h Shows this message.");
+ Console.WriteLine("-s 🐍");
+ return 0;
+ }
+
+ // Parsing Arguments
+ var bucketName = args[0] + "-3e5b3069-214a-43ee-8d85-57d72000c19d";
+ var startTime = Int64.Parse(args[1]);
+ var endTime = Int64.Parse(args[2]);
+ var numberOfDataPoints = Int64.Parse(args[3]);
+
+ var timeBetweenDataPoints = TimeBetweenDataPoints(startTime, endTime, numberOfDataPoints);
+
+ // Building a List of the timestamps we want to grab the files for.
+ var timestampList = new List { };
+ for (var i = startTime; i <= endTime; i += timeBetweenDataPoints)
+ {
+ //Rounding to even numbers only (we only save every second second)
+ timestampList.Add((i/2 *2).ToString());
+ }
+
+ await PrintFiles(bucketName,timestampList);
+
+ // Success
+ return 0;
+ }
+
+ private static async Task PrintFiles(String bucketName, List timestampList)
+ {
+ var newestDataFilename = timestampList.Last();
+ var csvFileText = await GetFileText(bucketName, newestDataFilename);
+
+ // Building Header-Row from the newest data
+ csvFileText
+ .Select(l => l.Split(";"))
+ .Select(l => l[0])
+ .Prepend("Timestamp")
+ .JoinWith(";")
+ .WriteLine();
+
+ foreach (var timestamp in timestampList)
+ {
+ csvFileText = await GetFileText(bucketName, timestamp);
+
+ // Writing Data below data-keys in a timestamped row
+ csvFileText.Select(l => l.Split(";"))
+ .Select(l => l[1])
+ .Prepend(timestamp)
+ .JoinWith(";")
+ .WriteLine();
+ }
+
+ }
+
+ private static Int64 TimeBetweenDataPoints(Int64 startTime, Int64 endTime, Int64 numberOfDataPoints)
+ {
+ // Calculating temporal distance of data files from the number of requested points.
+ var timeSpan = endTime - startTime;
+ var timeBetweenDataPoints = timeSpan / numberOfDataPoints;
+
+ // We only upload data every second second so sampling more is impossible.
+ // If this ever changes we might have to change this as well.
+ timeBetweenDataPoints = Math.Max(timeBetweenDataPoints, 2);
+ return timeBetweenDataPoints;
+ }
+
+ // This Method extracts the Text from a given csv file on the s3 bucket
+ private static async Task GetFileText(String bucketName, String filename)
+ {
+ return await S3Access.Admin.GetFileText(bucketName, filename + ".csv");
+ }
+}
\ No newline at end of file
diff --git a/csharp/App/S3Explorer/S3Explorer.csproj b/csharp/App/S3Explorer/S3Explorer.csproj
new file mode 100644
index 000000000..3c32de060
--- /dev/null
+++ b/csharp/App/S3Explorer/S3Explorer.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/csharp/App/S3Explorer/SnakeGameSS.cs b/csharp/App/S3Explorer/SnakeGameSS.cs
new file mode 100644
index 000000000..60d5eb253
--- /dev/null
+++ b/csharp/App/S3Explorer/SnakeGameSS.cs
@@ -0,0 +1,283 @@
+namespace S3Explorer;
+
+public static class SnakeGameSs
+{
+ public static async Task PlaySnake()
+ {
+ var tickRate = TimeSpan.FromMilliseconds(100);
+ var snakeGame = new SnakeGame();
+
+ using (var cts = new CancellationTokenSource())
+ {
+ async Task MonitorKeyPresses()
+ {
+ while (!cts.Token.IsCancellationRequested)
+ {
+ if (Console.KeyAvailable)
+ {
+ var key = Console.ReadKey(intercept: true).Key;
+ snakeGame.OnKeyPress(key);
+ }
+
+ await Task.Delay(10);
+ }
+ }
+
+ var monitorKeyPresses = MonitorKeyPresses();
+
+ do
+ {
+ snakeGame.OnGameTick();
+ snakeGame.Render();
+ await Task.Delay(tickRate);
+ } while (!snakeGame.GameOver);
+
+ // Allow time for user to weep before application exits.
+ for (var i = 0; i < 3; i++)
+ {
+ Console.Clear();
+ await Task.Delay(500);
+ snakeGame.Render();
+ await Task.Delay(500);
+ }
+
+ cts.Cancel();
+ await monitorKeyPresses;
+ }
+ }
+
+ enum Direction
+ {
+ Up,
+ Down,
+ Left,
+ Right
+ }
+
+ interface IRenderable
+ {
+ void Render();
+ }
+
+ readonly struct Position
+ {
+ public Position(int top, int left)
+ {
+ Top = top;
+ Left = left;
+ }
+
+ public int Top { get; }
+ public int Left { get; }
+
+ public Position RightBy(int n) => new Position(Top, Left + n);
+ public Position DownBy(int n) => new Position(Top + n, Left);
+ }
+
+ class Apple : IRenderable
+ {
+ public Apple(Position position)
+ {
+ Position = position;
+ }
+
+ public Position Position { get; }
+
+ public void Render()
+ {
+ Console.SetCursorPosition(Position.Left, Position.Top);
+ Console.Write("🍏");
+ }
+ }
+
+ class Snake : IRenderable
+ {
+ private List _body;
+ private int _growthSpurtsRemaining;
+
+ public Snake(Position spawnLocation, int initialSize = 1)
+ {
+ _body = new List { spawnLocation };
+ _growthSpurtsRemaining = Math.Max(0, initialSize - 1);
+ Dead = false;
+ }
+
+ public bool Dead { get; private set; }
+ public Position Head => _body.First();
+ private IEnumerable Body => _body.Skip(1);
+
+ public void Move(Direction direction)
+ {
+ if (Dead) throw new InvalidOperationException();
+
+ Position newHead;
+
+ switch (direction)
+ {
+ case Direction.Up:
+ newHead = Head.DownBy(-1);
+ break;
+
+ case Direction.Left:
+ newHead = Head.RightBy(-1);
+ break;
+
+ case Direction.Down:
+ newHead = Head.DownBy(1);
+ break;
+
+ case Direction.Right:
+ newHead = Head.RightBy(1);
+ break;
+
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ if (_body.Contains(newHead) || !PositionIsValid(newHead))
+ {
+ Dead = true;
+ return;
+ }
+
+ _body.Insert(0, newHead);
+
+ if (_growthSpurtsRemaining > 0)
+ {
+ _growthSpurtsRemaining--;
+ }
+ else
+ {
+ _body.RemoveAt(_body.Count - 1);
+ }
+ }
+
+ public void Grow()
+ {
+ if (Dead) throw new InvalidOperationException();
+
+ _growthSpurtsRemaining++;
+ }
+
+ public void Render()
+ {
+ Console.SetCursorPosition(Head.Left, Head.Top);
+ Console.Write("◉");
+
+ foreach (var position in Body)
+ {
+ Console.SetCursorPosition(position.Left, position.Top);
+ Console.Write("■");
+ }
+ }
+
+ private static bool PositionIsValid(Position position) =>
+ position.Top >= 0 && position.Left >= 0;
+ }
+
+ class SnakeGame : IRenderable
+ {
+ private static readonly Position Origin = new Position(0, 0);
+
+ private Direction _currentDirection;
+ private Direction _nextDirection;
+ private Snake _snake;
+ private Apple _apple;
+
+ public SnakeGame()
+ {
+ _snake = new Snake(Origin, initialSize: 5);
+ _apple = CreateApple();
+ _currentDirection = Direction.Right;
+ _nextDirection = Direction.Right;
+ }
+
+ public bool GameOver => _snake.Dead;
+
+ public void OnKeyPress(ConsoleKey key)
+ {
+ Direction newDirection;
+
+ switch (key)
+ {
+ case ConsoleKey.W:
+ newDirection = Direction.Up;
+ break;
+
+ case ConsoleKey.A:
+ newDirection = Direction.Left;
+ break;
+
+ case ConsoleKey.S:
+ newDirection = Direction.Down;
+ break;
+
+ case ConsoleKey.D:
+ newDirection = Direction.Right;
+ break;
+
+ default:
+ return;
+ }
+
+ // Snake cannot turn 180 degrees.
+ if (newDirection == OppositeDirectionTo(_currentDirection))
+ {
+ return;
+ }
+
+ _nextDirection = newDirection;
+ }
+
+ public void OnGameTick()
+ {
+ if (GameOver) throw new InvalidOperationException();
+
+ _currentDirection = _nextDirection;
+ _snake.Move(_currentDirection);
+
+ // If the snake's head moves to the same position as an apple, the snake
+ // eats it.
+ if (_snake.Head.Equals(_apple.Position))
+ {
+ _snake.Grow();
+ _apple = CreateApple();
+ }
+ }
+
+ public void Render()
+ {
+ Console.Clear();
+ _snake.Render();
+ _apple.Render();
+ Console.SetCursorPosition(0, 0);
+ }
+
+ private static Direction OppositeDirectionTo(Direction direction)
+ {
+ switch (direction)
+ {
+ case Direction.Up: return Direction.Down;
+ case Direction.Left: return Direction.Right;
+ case Direction.Right: return Direction.Left;
+ case Direction.Down: return Direction.Up;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ private static Apple CreateApple()
+ {
+ // Can be factored elsewhere.
+ const int numberOfRows = 20;
+ const int numberOfColumns = 20;
+
+ var random = new Random();
+ var top = random.Next(0, numberOfRows + 1);
+ var left = random.Next(0, numberOfColumns + 1);
+ var position = new Position(top, left);
+ var apple = new Apple(position);
+
+ return apple;
+ }
+ }
+}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs
index ed99a83da..d195a75d6 100644
--- a/csharp/App/SaliMax/src/Program.cs
+++ b/csharp/App/SaliMax/src/Program.cs
@@ -73,14 +73,14 @@ internal static class Program
}
catch (Exception e)
{
- Console.WriteLine(e);
+ e.LogError();
}
}
}
private static async Task Run()
{
- Console.WriteLine("Starting SaliMax");
+ "Starting SaliMax".LogInfo();
// Send the initial "service started" message to systemd
var sdNotifyReturn = sd_notify(0, "READY=1");
@@ -99,25 +99,25 @@ internal static class Program
StatusRecord ReadStatus()
{
- Console.WriteLine(" Reading AcDC");
+ "Reading AcDC".LogInfo();
var acDc = acDcDevices.Read();
- Console.WriteLine(" Reading dcDc");
+ "Reading dcDc".LogInfo();
var dcDc = dcDcDevices.Read();
- Console.WriteLine(" Reading battery");
+ "Reading battery".LogInfo();
var battery = batteryDevices.Read();
- Console.WriteLine(" Reading relays");
+ "Reading relays".LogInfo();
var relays = saliMaxRelaysDevice.Read();
- Console.WriteLine(" loadOnAcIsland");
+ "Reading loadOnAcIsland".LogInfo();
var loadOnAcIsland = acIslandLoadMeter.Read();
- Console.WriteLine(" Reading gridMeter");
+ "Reading gridMeter".LogInfo();
var gridMeter = gridMeterDevice.Read();
- Console.WriteLine(" Reading pvOnDc");
+ "Reading pvOnDc".LogInfo();
var pvOnDc = amptDevice.Read();
var pvOnAcGrid = AcPowerDevice.Null;
@@ -177,30 +177,35 @@ internal static class Program
dcDcDevices.Write(r.DcDc);
}
-
+ const Int32 delayTime = 10;
+
Console.WriteLine("press ctrl-C to stop");
while (true)
{
sd_notify(0, "WATCHDOG=1");
- var t = UnixTime.FromTicks(UnixTime.Now.Ticks / 2 * 2);
-
- //t.ToUtcDateTime().WriteLine();
-
+ var t = UnixTime.Now;
+ while (t.Ticks % UpdateIntervalSeconds != 0)
+ {
+ await Task.Delay(delayTime);
+ t = UnixTime.Now;
+ }
+
+
+
var record = ReadStatus();
- PrintTopology(record);
-
if (record.Relays is not null)
- record.Relays.ToCsv().WriteLine();
+ record.Relays.ToCsv().LogInfo();
record.ControlConstants();
record.ControlSystemState();
- Console.WriteLine($"{record.StateMachine.State}: {record.StateMachine.Message}");
-
- var essControl = record.ControlEss().WriteLine();
+ (t + "\n").LogInfo();
+ $"{record.StateMachine.State}: {record.StateMachine.Message}".LogInfo();
+ $"Batteries SOC: {record.Battery.Soc}".LogInfo();
+ var essControl = record.ControlEss().LogInfo();
record.EssControl = essControl;
@@ -211,11 +216,13 @@ internal static class Program
WriteControl(record);
+ PrintTopology(record);
+
await UploadCsv(record, t);
record.Config.Save();
- "===========================================".WriteLine();
+ "===========================================".LogInfo();
}
// ReSharper disable once FunctionNeverReturns
}
@@ -234,13 +241,32 @@ internal static class Program
var islandToGridBusPower = inverterPower + islandLoadPower;
var gridLoadPower = s.LoadOnAcGrid is null ? 0: s.LoadOnAcGrid.Power.Active;
- var gridPowerByPhase = TextBlock.AlignLeft(s.GridMeter.Ac.L1.Power.Active.ToDisplayString(),
+ TextBlock gridBusColumn;
+ TextBlock gridBox;
+ TextBlock totalBoxes;
+
+
+ if (s.GridMeter is not null)
+ {
+ var gridPowerByPhase = TextBlock.AlignLeft(s.GridMeter.Ac.L1.Power.Active.ToDisplayString(),
s.GridMeter.Ac.L2.Power.Active.ToDisplayString(),
s.GridMeter.Ac.L3.Power.Active.ToDisplayString());
+
+ var gridVoltageByPhase = TextBlock.AlignLeft(s.GridMeter.Ac.L1.Voltage.ToDisplayString(),
+ s.GridMeter.Ac.L2.Voltage.ToDisplayString(),
+ s.GridMeter.Ac.L3.Voltage.ToDisplayString());
+
+ gridBusColumn = ColumnBox("Pv", "Grid Bus", "Load" , gridVoltageByPhase , gridLoadPower);
+ gridBox = TextBlock.AlignLeft(gridPowerByPhase).TitleBox("Grid");
+
+ }
+ else
+ {
+ gridBusColumn = TextBlock.Spacer(0);
+ gridBox = TextBlock.Spacer(0);
+ }
+
- var gridVoltageByPhase = TextBlock.AlignLeft(s.GridMeter.Ac.L1.Voltage.ToDisplayString(),
- s.GridMeter.Ac.L2.Voltage.ToDisplayString(),
- s.GridMeter.Ac.L3.Voltage.ToDisplayString());
var inverterPowerByPhase = TextBlock.AlignLeft(s.AcDc.Ac.L1.Power.Active.ToDisplayString(),
s.AcDc.Ac.L2.Power.Active.ToDisplayString(),
@@ -264,7 +290,6 @@ internal static class Program
var anyBatteryAlarm = s.Battery.Alarms.Any();
var anyBatteryWarning = s.Battery.Warnings.Any();
- var gridBusColumn = ColumnBox("Pv", "Grid Bus", "Load" , gridVoltageByPhase , gridLoadPower);
var islandBusColumn = ColumnBox("Pv", "Island Bus", "Load" , inverterPowerByPhase, islandLoadPower);
var dcBusColumn = ColumnBox("Pv", "Dc Bus", "Load" , dcLinkVoltage, 0, pvOnDcPower);
var gridBusFlow = Flow.Horizontal(gridPower);
@@ -274,8 +299,7 @@ internal static class Program
var flowDcBusToDcDc = Flow.Horizontal(dcdcPower);
var flowDcDcToBattery = Flow.Horizontal(dcBatteryPower);
- var gridBox = TextBlock.AlignLeft(gridPowerByPhase).TitleBox("Grid");
- var inverterBox = TextBlock.AlignLeft(inverterPowerByAcDc).TitleBox("Inverter");
+ var inverterBox = TextBlock.AlignLeft(inverterPowerByAcDc).TitleBox("AC/DC");
var dcDcBox = TextBlock.AlignLeft(dc48Voltage).TitleBox("DC/DC");
var batteryAvgBox = TextBlock.AlignLeft(batteryVoltage,
batterySoc,
@@ -297,11 +321,27 @@ internal static class Program
var individualBatteries = batteryBoxes.Any()
? TextBlock.AlignLeft(batteryBoxes)
: TextBlock.Spacer(1);
-
- var totalBoxes = TextBlock.AlignCenterVertical(gridBox,
- gridBusFlow,
- gridBusColumn,
- flowGridBusToIslandBus,
+
+ if (s.GridMeter is not null)
+ {
+ totalBoxes = TextBlock.AlignCenterVertical(gridBox,
+ gridBusFlow,
+ gridBusColumn,
+ flowGridBusToIslandBus,
+ islandBusColumn,
+ flowIslandBusToInverter,
+ inverterBox,
+ flowInverterToDcBus,
+ dcBusColumn,
+ flowDcBusToDcDc,
+ dcDcBox,
+ flowDcDcToBattery,
+ batteryAvgBox,
+ individualBatteries);
+ }
+ else
+ {
+ totalBoxes = TextBlock.AlignCenterVertical(
islandBusColumn,
flowIslandBusToInverter,
inverterBox,
@@ -312,6 +352,8 @@ internal static class Program
flowDcDcToBattery,
batteryAvgBox,
individualBatteries);
+ }
+
totalBoxes.WriteLine();
}
@@ -363,10 +405,18 @@ internal static class Program
private static void ControlConstants(this StatusRecord r)
{
var inverters = r.AcDc.Devices;
+ var dcDevices = r.DcDc.Devices;
inverters.ForEach(d => d.Control.Dc.MaxVoltage = r.Config.MaxDcBusVoltage);
inverters.ForEach(d => d.Control.Dc.MinVoltage = r.Config.MinDcBusVoltage);
inverters.ForEach(d => d.Control.Dc.ReferenceVoltage = r.Config.ReferenceDcBusVoltage);
+
+ // dcDevices.ForEach(d => d.Control. Dc.MaxVoltage = r.Config.MaxDcBusVoltage);
+ // dcDevices.ForEach(d => d.Control. Dc.MinVoltage = r.Config.MinDcBusVoltage);
+ // dcDevices.ForEach(d => d.Control. Dc.ReferenceVoltage = r.Config.ReferenceDcBusVoltage);
+
+ r.DcDc.ResetAlarms();
+ r.AcDc.ResetAlarms();
}
@@ -408,11 +458,13 @@ internal static class Program
sc.UseSlaveIdForAddressing = true;
sc.SlaveErrorHandling = SlaveErrorHandling.Relaxed;
sc.SubSlaveErrorHandling = SubSlaveErrorHandling.Off;
+
sc.ResetAlarmsAndWarnings = true;
}
private static void ApplyDcDcDefaultSettings(this SystemControlRegisters? sc)
{
+
if (sc is null)
return;
@@ -427,7 +479,8 @@ internal static class Program
sc.UseSlaveIdForAddressing = true;
sc.SlaveErrorHandling = SlaveErrorHandling.Relaxed;
sc.SubSlaveErrorHandling = SubSlaveErrorHandling.Off;
- sc.ResetAlarmsAndWarnings = true;
+
+ sc.ResetAlarmsAndWarnings = true;
}
private static async Task UploadCsv(StatusRecord status, UnixTime timeStamp)
diff --git a/csharp/InnovEnergy.sln b/csharp/InnovEnergy.sln
index beaac246d..735ef04c7 100644
--- a/csharp/InnovEnergy.sln
+++ b/csharp/InnovEnergy.sln
@@ -77,6 +77,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adam6360D", "Lib\Devices\Ad
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IEM3kGridMeter", "Lib\Devices\IEM3kGridMeter\IEM3kGridMeter.csproj", "{1391165D-51F1-45B4-8B7F-042A20AA0277}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "S3Explorer", "App\S3Explorer\S3Explorer.csproj", "{EB56EF94-D8A7-4111-A8E7-A87EF13596DA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -196,6 +198,10 @@ Global
{1391165D-51F1-45B4-8B7F-042A20AA0277}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1391165D-51F1-45B4-8B7F-042A20AA0277}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1391165D-51F1-45B4-8B7F-042A20AA0277}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EB56EF94-D8A7-4111-A8E7-A87EF13596DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EB56EF94-D8A7-4111-A8E7-A87EF13596DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EB56EF94-D8A7-4111-A8E7-A87EF13596DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EB56EF94-D8A7-4111-A8E7-A87EF13596DA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{CF4834CB-91B7-4172-AC13-ECDA8613CD17} = {145597B4-3E30-45E6-9F72-4DD43194539A}
@@ -230,5 +236,6 @@ Global
{1A56992B-CB72-490F-99A4-DF1186BA3A18} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
{A3C79247-4CAA-44BE-921E-7285AB39E71F} = {4931A385-24DC-4E78-BFF4-356F8D6D5183}
{1391165D-51F1-45B4-8B7F-042A20AA0277} = {4931A385-24DC-4E78-BFF4-356F8D6D5183}
+ {EB56EF94-D8A7-4111-A8E7-A87EF13596DA} = {145597B4-3E30-45E6-9F72-4DD43194539A}
EndGlobalSection
EndGlobal