Salmon/Salmon.Core/Depth/StateMachine.cs
2024-05-19 17:28:04 +02:00

200 lines
4.8 KiB
C#

using System.Collections.Generic;
namespace Salmon.Core.Depth;
public class StateMachine
{
public Persistence? History { get; set; }
public EventManager? Events { get; set; }
class ImplValue
{
public object? Value { get; set; }
public DateTime When { get; set; } = DateTime.Now;
public ImplValue(object? value, DateTime? when = null)
{
Value = value;
When = when ?? DateTime.Now;
}
}
object Mutex = new object();
Dictionary<string, Dictionary<string, ImplValue>> Implementation = new ();
public bool Populated { get; private set; } = false;
public async Task PopulateWithHistory()
{
Populated = true;
if (History is not null)
await foreach (var i in History.GetState())
Push(i);
}
public void Push(Triplet triplet)
{
Push(triplet.key, triplet.predicate, triplet.value, triplet.LastFlush);
}
/// <summary>
/// Update string value in dictionary
/// Must be thread safe
/// </summary>
/// <param name="key">May not be null</param>
/// <param name="value">May be null</param>
public void Push(string key, string predicate, object? value, DateTime when)
{
if(key == null) throw new ArgumentNullException(nameof(key));
if (Get(key, predicate) == value)
return;
var implVal = new ImplValue(value, when);
lock (Mutex)
{
if(!Implementation.ContainsKey(key))
{
Dictionary<string, ImplValue> dic = new();
dic.Add(predicate, implVal);
Implementation.Add(key, dic);
}
var keyDic = Implementation[key];
if(!keyDic.TryAdd(predicate, implVal))
{
var old = keyDic[predicate];
if (old.When > when)
return;
keyDic[predicate] = implVal;
}
}
if(History is not null)
History.PushStateChange(key, predicate, value, when);
if (Events is not null)
Events.Push(Event.FromStateChange(key, value, when));
}
public IEnumerable<string> GetIds()
{
lock (Mutex)
{
if (!Populated)
PopulateWithHistory().Wait();
foreach (var kv in Implementation)
yield return kv.Key;
}
}
public IEnumerable<Triplet> Get()
{
lock (Mutex)
{
if (!Populated)
PopulateWithHistory().Wait();
foreach (var kv in Implementation)
foreach (var kvv in kv.Value)
yield return new Triplet
{
key = kv.Key,
predicate = kvv.Key,
value = kvv.Value.Value,
LastFlush = kvv.Value.When
};
}
}
public IEnumerable<Triplet> Get(string key)
{
Dictionary<string, ImplValue> dic;
lock (Mutex)
{
if (!Populated)
PopulateWithHistory().Wait();
if (!Implementation.TryGetValue(key, out dic))
yield break;
}
foreach(var kv in dic)
yield return new Triplet
{
key = key,
predicate = kv.Key,
value = kv.Value.Value,
LastFlush = kv.Value.When
};
}
public DateTime? GetLastUpdate(string key)
{
Dictionary<string, ImplValue> dic;
lock (Mutex)
{
if (!Populated)
PopulateWithHistory().Wait();
if (!Implementation.TryGetValue(key, out dic))
return null;
}
DateTime mostrecent = DateTime.MinValue;
foreach (var kv in dic)
if(kv.Value.When > mostrecent)
mostrecent = kv.Value.When;
if (mostrecent == DateTime.MinValue)
return null;
return mostrecent;
}
public object? Get(string key, string predicate)
{
lock (Mutex)
{
if (!Populated)
PopulateWithHistory().Wait();
if (Implementation.TryGetValue(key, out var value))
if (value.TryGetValue(predicate, out var ret))
return ret;
}
return null;
}
public long CountTriplets()
{
lock(Mutex)
{
if (!Populated)
PopulateWithHistory().Wait();
long c = 0;
foreach (var kv in Implementation)
foreach (var i in kv.Key)
c++;
return c;
}
}
}