Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[TAM-2039] Add affected sensors to table, add cached alert schedule r…
…esult for bar sensors
  • Loading branch information
Siarhei Hanich committed Mar 26, 2026
commit e3074aa5388b4efbc4c726ebd1f10d4ab15d0151
58 changes: 55 additions & 3 deletions src/server/HSMServer.Core/AlertSchedule/AlertScheduleProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ private class CacheEntry
public DateTime? CachedTime { get; set; }
public bool? WorkingTimeResult { get; set; }

public Dictionary<(DateTime StartTime, DateTime EndTime), bool> IntervalCache { get; set; } = new();


public bool IsCacheValid(DateTime time)
{
if (!CachedTime.HasValue || !WorkingTimeResult.HasValue)
Expand All @@ -33,7 +36,28 @@ public bool IsCacheValid(DateTime time)
return RoundToMinute(CachedTime.Value) == RoundToMinute(time);
}

private DateTime RoundToMinute(DateTime time)
public void AddIntervalToCache(DateTime startTime, DateTime endTime, bool result)
{
var key = (startTime, endTime);
IntervalCache[key] = result;
}

public bool? GetCachedIntervalResult(DateTime startTime, DateTime endTime)
{
var key = (startTime, endTime);
if (IntervalCache.TryGetValue(key, out var result))
return result;
return null;
}

public void InvalidateCache()
{
CachedTime = null;
WorkingTimeResult = null;
IntervalCache.Clear();
}

private static DateTime RoundToMinute(DateTime time)
{
return new DateTime(time.Year, time.Month, time.Day,
time.Hour, time.Minute, 0, time.Kind);
Expand Down Expand Up @@ -73,6 +97,35 @@ public bool IsWorkingTime(Guid id, DateTime time)
}
}

public bool IsWorkingTime(Guid id, DateTime startTime, DateTime endTime)
{
if (startTime >= endTime)
throw new ArgumentException("Start time must be less than end time");

lock (_lock)
{
if (_cache.TryGetValue(id, out var cacheEntry))
{
var cachedResult = cacheEntry.GetCachedIntervalResult(startTime, endTime);
if (cachedResult.HasValue)
{
return cachedResult.Value;
}

var result = cacheEntry.Schedule.IsWorkingTime(startTime, endTime);

cacheEntry.AddIntervalToCache(startTime, endTime, result);

return result;
}
else
{
_logger.Error($"Alert Schedule with id = {id} was not found.");
return true;
}
}
}

public void DeleteSchedule(Guid id)
{
lock (_lock)
Expand Down Expand Up @@ -106,8 +159,7 @@ public void SaveSchedule(AlertSchedule schedule)
if (_cache.TryGetValue(schedule.Id, out var cacheEntry))
{
cacheEntry.Schedule = schedule;
cacheEntry.CachedTime = null;
cacheEntry.WorkingTimeResult = null;
cacheEntry.InvalidateCache();
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ public interface IAlertScheduleProvider
void DeleteSchedule(Guid Id);

bool IsWorkingTime(Guid id, DateTime time);

bool IsWorkingTime(Guid id, DateTime startTime, DateTime endTime);
}
}
1 change: 1 addition & 0 deletions src/server/HSMServer.Core/Cache/ITreeValuesCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,5 +119,6 @@ public interface ITreeValuesCache

Task RunProductsSelfDestroyAsync(CancellationToken token = default);

List<BaseSensorModel> GetSensorsByAlertSchedule(Guid id);
}
}
23 changes: 23 additions & 0 deletions src/server/HSMServer.Core/Cache/TreeValuesCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2292,5 +2292,28 @@ private void RemoveProductFromCache(ProductModel product)
_cache.TryRemove(product.Id, out var value);
}

public List<BaseSensorModel> GetSensorsByAlertSchedule(Guid id)
{
var result = new List<BaseSensorModel>();
foreach (var (_, sensor) in _sensorsById)
{
if (sensor.Policies.TimeToLive.ScheduleId == id)
{
result.Add(sensor);
continue;
}


foreach (var policy in sensor.Policies)
{
if (policy.ScheduleId == id)
{
result.Add(sensor);
continue;
}
}
}
return result;
}
}
}
38 changes: 38 additions & 0 deletions src/server/HSMServer.Core/Model/Policies/AlertSchedule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,44 @@ public bool IsWorkingTime(DateTime dateTime)
timeOfDay >= w.Start && timeOfDay < w.End) ?? false;
}

public bool IsWorkingTime(DateTime startTime, DateTime endTime)
{
if (startTime >= endTime)
throw new ArgumentException("Start time must be less than end time");

var currentTime = startTime;
var end = endTime;

while (currentTime.Date <= end.Date)
{
var date = currentTime.Date;

var workingWindows = GetWorkingWindowsForDate(date);

if (workingWindows.Count != 0)
{
var dayStart = currentTime;
var dayEnd = currentTime.Date.AddDays(1);

if (date == end.Date)
dayEnd = end;

foreach (var window in workingWindows)
{
var windowStart = date.Add(window.Start);
var windowEnd = date.Add(window.End);

if (dayStart < windowEnd && dayEnd > windowStart)
return true;
}
}

currentTime = currentTime.Date.AddDays(1);
}

return false;
}

private DateTime ConvertUtcToLocalTime(DateTime utcDateTime)
{
var timezone = TimeZoneInfo.FindSystemTimeZoneById(Timezone);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ protected override bool CalculateStorageResult(ValueType value, bool isLastValue
bool schedulePassed = true;
if (policy.ScheduleId.HasValue)
{
schedulePassed = _scheduleProvider.IsWorkingTime(policy.ScheduleId.Value, value.LastUpdateTime);
if (value is BarBaseValue barValue)
schedulePassed = _scheduleProvider.IsWorkingTime(policy.ScheduleId.Value, barValue.OpenTime, barValue.CloseTime);
else
schedulePassed = _scheduleProvider.IsWorkingTime(policy.ScheduleId.Value, value.LastUpdateTime);
}

if (!schedulePassed)
Expand Down
26 changes: 20 additions & 6 deletions src/server/HSMServer/Controllers/AlertSchedulesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using HSMServer.Model.AlertSchedule;
using System.Collections.Generic;
using HSMServer.Core.Schedule;
using HSMServer.Core.Cache;



Expand All @@ -31,6 +32,7 @@ public sealed class AlertSchedulesController : BaseController

private readonly IAlertScheduleProvider _scheduleProvider;
private readonly AlertScheduleParser _parser = new();
private readonly ITreeValuesCache _cache;

static AlertSchedulesController()
{
Expand All @@ -40,17 +42,16 @@ static AlertSchedulesController()
_serializeOptions.Converters.Add(new JsonStringEnumConverter());
}

public AlertSchedulesController(IAlertScheduleProvider scheduleProvider, IUserManager users) : base(users)
public AlertSchedulesController(IAlertScheduleProvider scheduleProvider, IUserManager users, ITreeValuesCache cache) : base(users)
{
_scheduleProvider = scheduleProvider;
_cache = cache;
}

[HttpGet]
public IActionResult Index()
{
List<AlertScheduleViewModel> result = _scheduleProvider.GetAllSchedules().Select(x => new AlertScheduleViewModel(x)).ToList() ?? [];

return View(result);
return View(GetAlertScheduleList());
}

[HttpPost]
Expand Down Expand Up @@ -113,8 +114,21 @@ public IActionResult SavePartial(AlertScheduleViewModel model)
[HttpGet]
public IActionResult GetAlertSchedulesTable()
{
return PartialView("_AlertSchedulesTable", GetAlertScheduleList());
}


private List<AlertScheduleViewModel> GetAlertScheduleList()
{

var list = _scheduleProvider.GetAllSchedules().Select(x => new AlertScheduleViewModel(x)).ToList();
return PartialView("_AlertSchedulesTable", list);

foreach (var item in list)
{
item.Sensors = _cache.GetSensorsByAlertSchedule(item.Id);
}

return list;
}
}
}
}
28 changes: 9 additions & 19 deletions src/server/HSMServer/Model/AlertSchedule/AlertScheduleViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HSMDatabase.AccessManager.DatabaseEntities;
using HSMServer.Core.Model;
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
using System.Collections.Generic;
Expand All @@ -19,14 +20,17 @@ public class AlertScheduleViewModel
[Required]
public string Timezone { get; set; }

public List<SelectListItem> TimeZoneList { get; set; }

private static readonly Lazy<List<SelectListItem>> _timezones = new(InitTimeZoneList);

public List<SelectListItem> TimeZoneList => _timezones.Value;

public string Schedule { get; set; }

public List<BaseSensorModel> Sensors { get; set; } = new();

public AlertScheduleViewModel()
{
TimeZoneList = InitTimeZoneList();

Schedule ??= @"daySchedules:
- days: [Mon, Tue, Wed, Thu, Fri]
windows:
Expand All @@ -37,21 +41,7 @@ public AlertScheduleViewModel()
windows:
- { start: ""10:00"", end: ""14:00"" }

disabledDates: [""2026-02-11"", ""2026-02-23""]

overrides:
enabledDates: [""2026-03-20""]

customScheduleDates:
- date: ""2026-03-21""
scheduleType: ""Sat""

- date: ""2026-03-22""
scheduleType: ""Mon""

- date: ""2026-03-23""
windows:
- { start: ""11:00"", end: ""16:00"" }";
disabledDates: [""2026-02-11"", ""2026-02-23""]";
}

public AlertScheduleViewModel(AlertScheduleEntity entity) : this()
Expand All @@ -70,7 +60,7 @@ public AlertScheduleViewModel(Core.Model.Policies.AlertSchedule schedule) : this
Schedule = schedule.Schedule;
}

private List<SelectListItem> InitTimeZoneList()
private static List<SelectListItem> InitTimeZoneList()
{
return TimeZoneInfo.GetSystemTimeZones()
.Select(tz => new SelectListItem
Expand Down
6 changes: 6 additions & 0 deletions src/server/HSMServer/Views/AlertSchedules/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,11 @@
savePartial: '@Url.Action("SavePartial", "AlertSchedules")',
getTable: '@Url.Action("GetAlertSchedulesTable", "AlertSchedules")'
};

var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})

</script>

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<tr>
<th scope="col">Name</th>
<th scope="col">TimeZone</th>
<th scope="col">Affected sensors</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
Expand All @@ -18,6 +20,13 @@
<tr>
<td scope="row">@item.Name</td>
<td>@item.Timezone</td>
<td data-bs-toggle="tooltip" data-bs-html="true" data-bs-custom-class="alert-template-tooltip" title="@{
foreach (var sensor in item.Sensors)
{
@sensor.FullPath;
<br />
}
}">@item.Sensors.Count</td>
<td class="text-end">
<div>
<button class="btn" data-bs-toggle="dropdown" data-bs-auto-close="true" aria-haspopup="true" aria-expanded="false">
Expand Down
Loading