Access levels reset script (C#)
This commit is contained in:
parent
4fd000a002
commit
6fafdd334b
|
@ -0,0 +1,265 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MDP.SID.Scripting.Service;
|
||||
using GrpcScriptService;
|
||||
using Newtonsoft.Json;
|
||||
using System.Threading;
|
||||
using System.Net.Http;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
public class Interval
|
||||
{
|
||||
public TimeSpan Start { get; set; }
|
||||
public TimeSpan End { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
private readonly Interval[] _accessIntervals =
|
||||
{
|
||||
// Explanation -> TimeSpan(int hours, int minutes, int seconds)
|
||||
new Interval {Start = new TimeSpan(16, 55, 0), End = new TimeSpan(17, 00, 0), Name = "Breakfast"},
|
||||
new Interval {Start = new TimeSpan(17, 00, 0), End = new TimeSpan(17, 05, 0), Name = "Lunch"},
|
||||
new Interval {Start = new TimeSpan(17, 05, 0), End = new TimeSpan(17, 10, 0), Name = "Dinner"}
|
||||
};
|
||||
|
||||
private readonly string[] _canteenDoors =
|
||||
{
|
||||
"CANTEEN", "CANTEEN-DOOR-R-TUR-2 Ent"
|
||||
};
|
||||
|
||||
private string _path;
|
||||
|
||||
private SemaphoreSlim _semaphoreSlim = new(1);
|
||||
|
||||
public readonly TimeSpan AccessLevelsResetTime = new(17, 11, 0);
|
||||
|
||||
private Timer _timer;
|
||||
|
||||
private static ConcurrentDictionary<int, IList<string>> _failedAddOrUpdate = new();
|
||||
|
||||
private async Task ResetUsersAccessLevelsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await _semaphoreSlim.WaitAsync();
|
||||
|
||||
if (File.Exists(_path) is false)
|
||||
{
|
||||
Context.LogError("Users file not found");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var alNames = _accessIntervals.Select(a => a.Name);
|
||||
|
||||
var accessLevels = await Context.GetAccessLevelsAsync();
|
||||
|
||||
var updateEntities = accessLevels
|
||||
.Where(a => alNames.Contains(a.Name))
|
||||
.Select(a => new EntityUpdate { Id = a.Id, Update = true });
|
||||
|
||||
var text = await File.ReadAllTextAsync(_path);
|
||||
|
||||
var users = JsonConvert.DeserializeObject<Dictionary<int, IList<string>>>(text);
|
||||
|
||||
if (users is null || users.Any() is false)
|
||||
{
|
||||
Context.LogWarning("Users file empty");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var userIds = users.Keys.ToArray();
|
||||
|
||||
var configuration = new BulkUpdateUser();
|
||||
|
||||
configuration.AccessLevels.AddRange(updateEntities);
|
||||
|
||||
await Context.BulkUpdateUsersAsync(0, userIds.Length, userIds, configuration);
|
||||
|
||||
File.Delete(_path);
|
||||
|
||||
Context.LogInformation("Users file deleted after bulk update operation");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Context.LogError($"Failed reset users access levels. Exception: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphoreSlim.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddOrUpdateUser(int userId, string accessLevel)
|
||||
{
|
||||
try
|
||||
{
|
||||
_semaphoreSlim.Wait();
|
||||
|
||||
Dictionary<int, IList<string>> users;
|
||||
|
||||
if (File.Exists(_path) is false)
|
||||
{
|
||||
users = new Dictionary<int, IList<string>>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var text = File.ReadAllText(_path);
|
||||
|
||||
users = JsonConvert.DeserializeObject<Dictionary<int, IList<string>>>(text);
|
||||
|
||||
// no content
|
||||
if (users is null)
|
||||
{
|
||||
users = new Dictionary<int, IList<string>>();
|
||||
}
|
||||
}
|
||||
|
||||
AddFromFailed(users);
|
||||
|
||||
if (users.TryGetValue(userId, out var accessLevels))
|
||||
{
|
||||
if (accessLevels.Contains(accessLevel) is false)
|
||||
{
|
||||
accessLevels.Add(accessLevel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
users.Add(userId, new List<string> { accessLevel });
|
||||
}
|
||||
|
||||
var output = JsonConvert.SerializeObject(users);
|
||||
|
||||
File.WriteAllText(_path, output);
|
||||
|
||||
_failedAddOrUpdate.Clear();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Context.LogError($"Failed add or update user with Id: {userId} al: {accessLevel}. Exception: {ex.Message}");
|
||||
|
||||
AddToFailed(userId, accessLevel);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphoreSlim.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddToFailed(int userId, string accessLevel)
|
||||
{
|
||||
if (_failedAddOrUpdate.TryGetValue(userId, out var als))
|
||||
{
|
||||
if (als.Contains(accessLevel)) return;
|
||||
|
||||
var newAls = new List<string>(als) { accessLevel };
|
||||
|
||||
_failedAddOrUpdate.TryUpdate(userId, newAls, als);
|
||||
}
|
||||
else
|
||||
{
|
||||
_failedAddOrUpdate.TryAdd(userId, new List<string> { accessLevel });
|
||||
}
|
||||
}
|
||||
|
||||
private void AddFromFailed(Dictionary<int, IList<string>> users)
|
||||
{
|
||||
foreach (var user in _failedAddOrUpdate)
|
||||
{
|
||||
if (users.TryGetValue(user.Key, out var accessLevels))
|
||||
{
|
||||
foreach (var al in user.Value)
|
||||
{
|
||||
if (accessLevels.Contains(al)) continue;
|
||||
|
||||
accessLevels.Add(al);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
users.Add(user.Key, user.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void EventReceived(Event received)
|
||||
{
|
||||
if (received.TypeUid == "891adc81-6991-44c2-8aa4-f7a0792f77f5" && _canteenDoors.Contains(received.DoorName))
|
||||
{
|
||||
var accessTime = received.Time.ToDateTime().ToLocalTime().TimeOfDay;
|
||||
|
||||
var interval = _accessIntervals.FirstOrDefault(a => a.End >= accessTime && a.Start <= accessTime);
|
||||
|
||||
if (interval is null)
|
||||
{
|
||||
Context.LogDebug($"Al interval by access time not found. Access time: {accessTime}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var userAccessLevels = await Context.GetUserAccessLevelsAsync((int)received.UserId);
|
||||
|
||||
var userAccessLevel = userAccessLevels.FirstOrDefault(a => a.Name == interval.Name);
|
||||
|
||||
if (userAccessLevel is null)
|
||||
{
|
||||
Context.LogWarning($"Al for user with Id: {(int)received.UserId} not found");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.DeleteUserAccessLevelAsync(userAccessLevel.Id);
|
||||
|
||||
Context.LogDebug($"Al: {userAccessLevel.Name} removed for user with Id: {(int)received.UserId}");
|
||||
|
||||
AddOrUpdateUser((int)received.UserId, userAccessLevel.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnResetEvent()
|
||||
{
|
||||
_timer.Interval = new TimeSpan(24, 0, 0).TotalMilliseconds;
|
||||
|
||||
_timer.Start();
|
||||
|
||||
Context.LogInformation("Reset users al's event raised");
|
||||
|
||||
await ResetUsersAccessLevelsAsync();
|
||||
}
|
||||
|
||||
Context.OnEventReceived += EventReceived;
|
||||
|
||||
var timeToReset = AccessLevelsResetTime - DateTime.Now.TimeOfDay;
|
||||
|
||||
if (timeToReset.CompareTo(TimeSpan.Zero) < 0)
|
||||
{
|
||||
timeToReset = new TimeSpan(1, AccessLevelsResetTime.Hours, AccessLevelsResetTime.Minutes, AccessLevelsResetTime.Seconds) - DateTime.Now.TimeOfDay;
|
||||
}
|
||||
|
||||
Context.LogDebug($"Time until al reset first time hit (milliseconds): {timeToReset}");
|
||||
|
||||
_timer = new Timer(timeToReset.TotalMilliseconds);
|
||||
_timer.Elapsed += async (_, _) => await OnResetEvent();
|
||||
_timer.AutoReset = false;
|
||||
_timer.Start();
|
||||
|
||||
var scriptFilesPath = await Context.GetScriptFilesPathAsync();
|
||||
|
||||
_path = Path.Combine(scriptFilesPath, "Canteen.txt");
|
||||
|
||||
CancellationToken.WaitHandle.WaitOne();
|
||||
|
||||
_timer.Stop();
|
||||
|
||||
_timer.Dispose();
|
||||
|
||||
Context.LogInformation("Script stopped");
|
Loading…
Reference in New Issue