Add doormode schudeling

This commit is contained in:
Martijn Scheepers
2025-10-17 08:48:13 +02:00
parent 6aca09d6d4
commit f379a9f689
76 changed files with 3057 additions and 116 deletions

1
.gitignore vendored
View File

@@ -22,3 +22,4 @@ chartjs-plugin-datalabels
/Aperio_Control_Centre/wwwroot/openlayers
/Publish
/Aperio_Control_Centre/wwwroot/ix-icons
/Aperio_Control_Centre/wwwroot/js

View File

@@ -27,6 +27,7 @@
<ItemGroup>
<ProjectReference Include="..\Aperio_Control_Centre.ACSDatabase\Aperio_Control_Centre.ACSDatabase.csproj" />
<ProjectReference Include="..\Aperio_Control_Centre\Aperio_Control_Centre.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,6 @@
using Aperio_Control_Centre.ACSDatabase.Models;
using Aperio_Control_Centre.ACSDatabase.Types;
using Aperio_Control_Centre.Extensions;
namespace Aperio_Control_Centre.ACSDatabase.UnitTest
{
@@ -13,13 +15,16 @@ namespace Aperio_Control_Centre.ACSDatabase.UnitTest
Id = 1,
BeginTime = TimeSpan.Zero,
EndTime = TimeSpan.FromDays(1) - TimeSpan.FromMilliseconds(1),
WeekDays = Types.WeekDaysTypes.Daily,
WeekDays = WeekDaysTypes.Daily,
Name = "Tijdzone",
TwentyFourHours = Types.TwentyFourHoursTypes.TwentyFourHours,
Active = Types.ActiveStates.Active,
TwentyFourHours = TwentyFourHoursTypes.TwentyFourHours,
Active = ActiveStates.Active,
Info = "Tijdzone",
LastChanged = DateTime.UtcNow
};
Assert.AreEqual("24 uur (dagelijks)", timezone.GetTimezoneString);
Assert.IsTrue(timezone.IsTimezoneActive(new DateTime(2022, 9, 20)));
}
@@ -31,13 +36,16 @@ namespace Aperio_Control_Centre.ACSDatabase.UnitTest
Id = 1,
BeginTime = TimeSpan.Zero,
EndTime = TimeSpan.FromDays(1) - TimeSpan.FromMilliseconds(1),
WeekDays = Types.WeekDaysTypes.None,
WeekDays = WeekDaysTypes.None,
Name = "Tijdzone",
TwentyFourHours = Types.TwentyFourHoursTypes.TwentyFourHours,
Active = Types.ActiveStates.Active,
TwentyFourHours = TwentyFourHoursTypes.TwentyFourHours,
Active = ActiveStates.Active,
Info = "Tijdzone",
LastChanged = DateTime.UtcNow
};
Assert.AreEqual("24 uur (nooit)", timezone.GetTimezoneString);
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 20)));
}
@@ -49,13 +57,16 @@ namespace Aperio_Control_Centre.ACSDatabase.UnitTest
Id = 1,
BeginTime = TimeSpan.Zero,
EndTime = TimeSpan.FromDays(1) - TimeSpan.FromMilliseconds(1),
WeekDays = Types.WeekDaysTypes.Monday,
WeekDays = WeekDaysTypes.Monday,
Name = "Tijdzone",
TwentyFourHours = Types.TwentyFourHoursTypes.TwentyFourHours,
Active = Types.ActiveStates.Active,
TwentyFourHours = TwentyFourHoursTypes.TwentyFourHours,
Active = ActiveStates.Active,
Info = "Tijdzone",
LastChanged = DateTime.UtcNow
};
Assert.AreEqual("24 uur (ma)", timezone.GetTimezoneString);
Assert.IsTrue(timezone.IsTimezoneActive(new DateTime(2022, 9, 19)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 20)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 21)));
@@ -73,13 +84,16 @@ namespace Aperio_Control_Centre.ACSDatabase.UnitTest
Id = 1,
BeginTime = TimeSpan.Zero,
EndTime = TimeSpan.FromDays(1) - TimeSpan.FromMilliseconds(1),
WeekDays = Types.WeekDaysTypes.Tuesday,
WeekDays = WeekDaysTypes.Tuesday,
Name = "Tijdzone",
TwentyFourHours = Types.TwentyFourHoursTypes.TwentyFourHours,
Active = Types.ActiveStates.Active,
TwentyFourHours = TwentyFourHoursTypes.TwentyFourHours,
Active = ActiveStates.Active,
Info = "Tijdzone",
LastChanged = DateTime.UtcNow
};
Assert.AreEqual("24 uur (di)", timezone.GetTimezoneString);
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 19)));
Assert.IsTrue(timezone.IsTimezoneActive(new DateTime(2022, 9, 20)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 21)));
@@ -97,13 +111,16 @@ namespace Aperio_Control_Centre.ACSDatabase.UnitTest
Id = 1,
BeginTime = TimeSpan.Zero,
EndTime = TimeSpan.FromDays(1) - TimeSpan.FromMilliseconds(1),
WeekDays = Types.WeekDaysTypes.Wednesday,
WeekDays = WeekDaysTypes.Wednesday,
Name = "Tijdzone",
TwentyFourHours = Types.TwentyFourHoursTypes.TwentyFourHours,
Active = Types.ActiveStates.Active,
TwentyFourHours = TwentyFourHoursTypes.TwentyFourHours,
Active = ActiveStates.Active,
Info = "Tijdzone",
LastChanged = DateTime.UtcNow
};
Assert.AreEqual("24 uur (wo)", timezone.GetTimezoneString);
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 19)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 20)));
Assert.IsTrue(timezone.IsTimezoneActive(new DateTime(2022, 9, 21)));
@@ -121,13 +138,16 @@ namespace Aperio_Control_Centre.ACSDatabase.UnitTest
Id = 1,
BeginTime = TimeSpan.Zero,
EndTime = TimeSpan.FromDays(1) - TimeSpan.FromMilliseconds(1),
WeekDays = Types.WeekDaysTypes.Thursday,
WeekDays = WeekDaysTypes.Thursday,
Name = "Tijdzone",
TwentyFourHours = Types.TwentyFourHoursTypes.TwentyFourHours,
Active = Types.ActiveStates.Active,
TwentyFourHours = TwentyFourHoursTypes.TwentyFourHours,
Active = ActiveStates.Active,
Info = "Tijdzone",
LastChanged = DateTime.UtcNow
};
Assert.AreEqual("24 uur (do)", timezone.GetTimezoneString);
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 19)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 20)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 21)));
@@ -145,13 +165,16 @@ namespace Aperio_Control_Centre.ACSDatabase.UnitTest
Id = 1,
BeginTime = TimeSpan.Zero,
EndTime = TimeSpan.FromDays(1) - TimeSpan.FromMilliseconds(1),
WeekDays = Types.WeekDaysTypes.Friday,
WeekDays = WeekDaysTypes.Friday,
Name = "Tijdzone",
TwentyFourHours = Types.TwentyFourHoursTypes.TwentyFourHours,
Active = Types.ActiveStates.Active,
TwentyFourHours = TwentyFourHoursTypes.TwentyFourHours,
Active = ActiveStates.Active,
Info = "Tijdzone",
LastChanged = DateTime.UtcNow
};
Assert.AreEqual("24 uur (vr)", timezone.GetTimezoneString);
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 19)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 20)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 21)));
@@ -169,13 +192,16 @@ namespace Aperio_Control_Centre.ACSDatabase.UnitTest
Id = 1,
BeginTime = TimeSpan.Zero,
EndTime = TimeSpan.FromDays(1) - TimeSpan.FromMilliseconds(1),
WeekDays = Types.WeekDaysTypes.Saturday,
WeekDays = WeekDaysTypes.Saturday,
Name = "Tijdzone",
TwentyFourHours = Types.TwentyFourHoursTypes.TwentyFourHours,
Active = Types.ActiveStates.Active,
TwentyFourHours = TwentyFourHoursTypes.TwentyFourHours,
Active = ActiveStates.Active,
Info = "Tijdzone",
LastChanged = DateTime.UtcNow
};
Assert.AreEqual("24 uur (za)", timezone.GetTimezoneString);
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 19)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 20)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 21)));
@@ -193,13 +219,16 @@ namespace Aperio_Control_Centre.ACSDatabase.UnitTest
Id = 1,
BeginTime = TimeSpan.Zero,
EndTime = TimeSpan.FromDays(1) - TimeSpan.FromMilliseconds(1),
WeekDays = Types.WeekDaysTypes.Sunday,
WeekDays = WeekDaysTypes.Sunday,
Name = "Tijdzone",
TwentyFourHours = Types.TwentyFourHoursTypes.TwentyFourHours,
Active = Types.ActiveStates.Active,
TwentyFourHours = TwentyFourHoursTypes.TwentyFourHours,
Active = ActiveStates.Active,
Info = "Tijdzone",
LastChanged = DateTime.UtcNow
};
Assert.AreEqual("24 uur (zo)", timezone.GetTimezoneString);
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 19)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 20)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 21)));
@@ -217,13 +246,16 @@ namespace Aperio_Control_Centre.ACSDatabase.UnitTest
Id = 1,
BeginTime = TimeSpan.Zero,
EndTime = TimeSpan.FromHours(12),
WeekDays = Types.WeekDaysTypes.Daily,
WeekDays = WeekDaysTypes.Daily,
Name = "Tijdzone",
TwentyFourHours = Types.TwentyFourHoursTypes.Timeselected,
Active = Types.ActiveStates.Active,
TwentyFourHours = TwentyFourHoursTypes.Timeselected,
Active = ActiveStates.Active,
Info = "Tijdzone",
LastChanged = DateTime.UtcNow
};
Assert.AreEqual("00:00 - 12:00 (dagelijks)", timezone.GetTimezoneString);
Assert.IsTrue(timezone.IsTimezoneActive(new DateTime(2022, 9, 19, 1, 1, 1)));
Assert.IsFalse(timezone.IsTimezoneActive(new DateTime(2022, 9, 19, 12, 1, 1)));
@@ -234,5 +266,302 @@ namespace Aperio_Control_Centre.ACSDatabase.UnitTest
Assert.IsTrue(timezone.IsTimezoneActive(new DateTime(2022, 9, 19, 12, 1, 1)));
}
[TestMethod]
public void Daily_Timespan_Test()
{
Timezone timezone = new()
{
BeginTime = TimeSpan.FromHours(12),
WeekDays = WeekDaysTypes.Daily,
TwentyFourHours = TwentyFourHoursTypes.Timeselected,
};
Assert.AreEqual("12:00 - 00:00 (dagelijks)", timezone.GetTimezoneString);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(new DateTime(2025, 1, 1, 12, 0, 0)).TotalSeconds);
Assert.AreEqual((uint)0, timezone.TimeToNextBeginTime(new DateTime(2025, 1, 1, 12, 0, 0)).ToDeciSeconds());
Assert.AreEqual(3600, timezone.TimeToNextBeginTime(new DateTime(2025, 1, 1, 11, 0, 0)).TotalSeconds);
Assert.AreEqual((uint)36000, timezone.TimeToNextBeginTime(new DateTime(2025, 1, 1, 11, 0, 0)).ToDeciSeconds());
Assert.AreEqual(43200, timezone.TimeToNextBeginTime(new DateTime(2025, 1, 1, 0, 0, 0)).TotalSeconds);
Assert.AreEqual((uint)432000, timezone.TimeToNextBeginTime(new DateTime(2025, 1, 1, 0, 0, 0)).ToDeciSeconds());
Assert.AreEqual(82800, timezone.TimeToNextBeginTime(new DateTime(2025, 1, 1, 13, 0, 0)).TotalSeconds);
Assert.AreEqual((uint)828000, timezone.TimeToNextBeginTime(new DateTime(2025, 1, 1, 13, 0, 0)).ToDeciSeconds());
Assert.AreEqual(86399, timezone.TimeToNextBeginTime(new DateTime(2025, 1, 1, 12, 0, 1)).TotalSeconds);
Assert.AreEqual((uint)863990, timezone.TimeToNextBeginTime(new DateTime(2025, 1, 1, 12, 0, 1)).ToDeciSeconds());
}
[TestMethod]
public void Day_Timespan_days_Test()
{
Timezone timezone = new()
{
BeginTime = TimeSpan.FromHours(8),
Monday = true,
Tuesday = true,
Wednesday = true,
Thursday = true,
Friday = true,
Saturday = false,
Sunday = false,
TwentyFourHours = TwentyFourHoursTypes.Timeselected,
};
Assert.AreEqual("08:00 - 00:00 (ma,di,wo,do,vr)", timezone.GetTimezoneString);
DateTime monday = new(2025, 9, 15, 12, 0, 0);
Assert.AreEqual(72000, timezone.TimeToNextBeginTime(monday).TotalSeconds);
DateTime tuesday = new(2025, 9, 16, 12, 0, 0);
Assert.AreEqual(72000, timezone.TimeToNextBeginTime(tuesday).TotalSeconds);
DateTime wednesday = new(2025, 9, 17, 12, 0, 0);
Assert.AreEqual(72000, timezone.TimeToNextBeginTime(wednesday).TotalSeconds);
DateTime thursday = new(2025, 9, 18, 12, 0, 0);
Assert.AreEqual(72000, timezone.TimeToNextBeginTime(thursday).TotalSeconds);
DateTime friday = new(2025, 9, 19, 12, 0, 0);
Assert.AreEqual(244800, timezone.TimeToNextBeginTime(friday).TotalSeconds);
DateTime saturday = new(2025, 9, 20, 12, 0, 0);
Assert.AreEqual(158400, timezone.TimeToNextBeginTime(saturday).TotalSeconds);
DateTime sunday = new(2025, 9, 21, 12, 0, 0);
Assert.AreEqual(72000, timezone.TimeToNextBeginTime(sunday).TotalSeconds);
}
[TestMethod]
public void Day_Timespan_None_Test()
{
Timezone timezone = new()
{
BeginTime = TimeSpan.FromHours(8),
Monday = false,
Tuesday = false,
Wednesday = false,
Thursday = false,
Friday = false,
Saturday = false,
Sunday = false,
TwentyFourHours = TwentyFourHoursTypes.Timeselected,
};
Assert.AreEqual("08:00 - 00:00 (nooit)", timezone.GetTimezoneString);
DateTime monday = new(2025, 9, 15, 12, 0, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(monday).TotalSeconds);
DateTime tuesday = new(2025, 9, 16, 12, 0, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(tuesday).TotalSeconds);
DateTime wednesday = new(2025, 9, 17, 12, 0, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(wednesday).TotalSeconds);
DateTime thursday = new(2025, 9, 18, 12, 0, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(thursday).TotalSeconds);
DateTime friday = new(2025, 9, 19, 12, 0, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(friday).TotalSeconds);
DateTime saturday = new(2025, 9, 20, 12, 0, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(saturday).TotalSeconds);
DateTime sunday = new(2025, 9, 21, 12, 0, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(sunday).TotalSeconds);
}
[TestMethod]
public void Day_Timespan_SingleDay_Test()
{
Timezone timezone = new()
{
BeginTime = TimeSpan.FromHours(12),
Monday = false,
Tuesday = false,
Wednesday = true,
Thursday = false,
Friday = false,
Saturday = false,
Sunday = false,
TwentyFourHours = TwentyFourHoursTypes.Timeselected,
};
Assert.AreEqual("12:00 - 00:00 (wo)", timezone.GetTimezoneString);
DateTime monday = new(2025, 9, 15, 0, 0, 0);
Assert.AreEqual(2, timezone.TimeToNextBeginTime(monday).Days);
Assert.AreEqual(12, timezone.TimeToNextBeginTime(monday).Hours);
DateTime tuesday = new(2025, 9, 16, 0, 0, 0);
Assert.AreEqual(1, timezone.TimeToNextBeginTime(tuesday).Days);
Assert.AreEqual(12, timezone.TimeToNextBeginTime(tuesday).Hours);
DateTime wednesday = new(2025, 9, 17, 0, 0, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(wednesday).Days);
Assert.AreEqual(12, timezone.TimeToNextBeginTime(wednesday).Hours);
DateTime thursday = new(2025, 9, 18, 0, 0, 0);
Assert.AreEqual(6, timezone.TimeToNextBeginTime(thursday).Days);
Assert.AreEqual(12, timezone.TimeToNextBeginTime(thursday).Hours);
DateTime friday = new(2025, 9, 19, 0, 0, 0);
Assert.AreEqual(5, timezone.TimeToNextBeginTime(friday).Days);
Assert.AreEqual(12, timezone.TimeToNextBeginTime(friday).Hours);
DateTime saturday = new(2025, 9, 20, 0, 0, 0);
Assert.AreEqual(4, timezone.TimeToNextBeginTime(saturday).Days);
Assert.AreEqual(12, timezone.TimeToNextBeginTime(saturday).Hours);
DateTime sunday = new(2025, 9, 21, 0, 0, 0);
Assert.AreEqual(3, timezone.TimeToNextBeginTime(sunday).Days);
Assert.AreEqual(12, timezone.TimeToNextBeginTime(sunday).Hours);
}
[TestMethod]
public void Day_Timespan_SingleDay_Time_Test()
{
Timezone timezone = new()
{
BeginTime = TimeSpan.FromHours(12),
Monday = false,
Tuesday = false,
Wednesday = true,
Thursday = false,
Friday = false,
Saturday = false,
Sunday = false,
TwentyFourHours = TwentyFourHoursTypes.Timeselected,
};
Assert.AreEqual("12:00 - 00:00 (wo)", timezone.GetTimezoneString);
DateTime monday = new(2025, 9, 15, 10, 0, 0);
Assert.AreEqual(2, timezone.TimeToNextBeginTime(monday).Days);
Assert.AreEqual(2, timezone.TimeToNextBeginTime(monday).Hours);
DateTime tuesday = new(2025, 9, 16, 11, 0, 0);
Assert.AreEqual(1, timezone.TimeToNextBeginTime(tuesday).Days);
Assert.AreEqual(1, timezone.TimeToNextBeginTime(tuesday).Hours);
DateTime wednesday = new(2025, 9, 17, 12, 0, 0);
Assert.AreEqual(7, timezone.TimeToNextBeginTime(wednesday).Days);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(wednesday).Hours);
DateTime thursday = new(2025, 9, 18, 13, 0, 0);
Assert.AreEqual(5, timezone.TimeToNextBeginTime(thursday).Days);
Assert.AreEqual(23, timezone.TimeToNextBeginTime(thursday).Hours);
DateTime friday = new(2025, 9, 19, 14, 0, 0);
Assert.AreEqual(4, timezone.TimeToNextBeginTime(friday).Days);
Assert.AreEqual(22, timezone.TimeToNextBeginTime(friday).Hours);
DateTime saturday = new(2025, 9, 20, 15, 0, 0);
Assert.AreEqual(3, timezone.TimeToNextBeginTime(saturday).Days);
Assert.AreEqual(21, timezone.TimeToNextBeginTime(saturday).Hours);
DateTime sunday = new(2025, 9, 21, 16, 0, 0);
Assert.AreEqual(2, timezone.TimeToNextBeginTime(sunday).Days);
Assert.AreEqual(20, timezone.TimeToNextBeginTime(sunday).Hours);
}
[TestMethod]
public void Day_Timespan_WorkDays_Time_Test()
{
Timezone timezone = new()
{
BeginTime = TimeSpan.FromHours(8),
EndTime = TimeSpan.FromHours(16).Add(TimeSpan.FromMinutes(30)),
Monday = true,
Tuesday = true,
Wednesday = true,
Thursday = true,
Friday = true,
Saturday = false,
Sunday = false,
TwentyFourHours = TwentyFourHoursTypes.Timeselected,
};
Assert.AreEqual("08:00", timezone.BeginTimeString());
Assert.AreEqual("16:30", timezone.EndTimeString());
Assert.AreEqual("08:00 - 16:30 (ma,di,wo,do,vr)", timezone.GetTimezoneString);
Assert.AreEqual("ma,di,wo,do,vr", timezone.GetWeekdayShortString);
DateTime monday = new(2025, 9, 22, 8, 30, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(monday).Days);
Assert.AreEqual(23, timezone.TimeToNextBeginTime(monday).Hours);
Assert.AreEqual(30, timezone.TimeToNextBeginTime(monday).Minutes);
Assert.AreEqual("23:hr 30:min 0:sec", timezone.TimeToNextBeginTime(monday).ToCustomString());
Assert.AreEqual(0, timezone.TimeToNextEndTime(monday).Days);
Assert.AreEqual(8, timezone.TimeToNextEndTime(monday).Hours);
Assert.AreEqual(0, timezone.TimeToNextEndTime(monday).Minutes);
Assert.AreEqual("8:hr 0:min 0:sec", timezone.TimeToNextEndTime(monday).ToCustomString());
DateTime tuesday = new(2025, 9, 23, 8, 30, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(tuesday).Days);
Assert.AreEqual(23, timezone.TimeToNextBeginTime(tuesday).Hours);
Assert.AreEqual(30, timezone.TimeToNextBeginTime(tuesday).Minutes);
Assert.AreEqual("23:hr 30:min 0:sec", timezone.TimeToNextBeginTime(tuesday).ToCustomString());
Assert.AreEqual(0, timezone.TimeToNextEndTime(tuesday).Days);
Assert.AreEqual(8, timezone.TimeToNextEndTime(tuesday).Hours);
Assert.AreEqual(0, timezone.TimeToNextEndTime(tuesday).Minutes);
Assert.AreEqual("8:hr 0:min 0:sec", timezone.TimeToNextEndTime(tuesday).ToCustomString());
DateTime wednesday = new(2025, 9, 24, 8, 30, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(wednesday).Days);
Assert.AreEqual(23, timezone.TimeToNextBeginTime(wednesday).Hours);
Assert.AreEqual(30, timezone.TimeToNextBeginTime(wednesday).Minutes);
Assert.AreEqual("23:hr 30:min 0:sec", timezone.TimeToNextBeginTime(wednesday).ToCustomString());
Assert.AreEqual(0, timezone.TimeToNextEndTime(wednesday).Days);
Assert.AreEqual(8, timezone.TimeToNextEndTime(wednesday).Hours);
Assert.AreEqual(0, timezone.TimeToNextEndTime(wednesday).Minutes);
Assert.AreEqual("8:hr 0:min 0:sec", timezone.TimeToNextEndTime(wednesday).ToCustomString());
DateTime thursday = new(2025, 9, 25, 8, 30, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(thursday).Days);
Assert.AreEqual(23, timezone.TimeToNextBeginTime(thursday).Hours);
Assert.AreEqual(30, timezone.TimeToNextBeginTime(thursday).Minutes);
Assert.AreEqual("23:hr 30:min 0:sec", timezone.TimeToNextBeginTime(thursday).ToCustomString());
Assert.AreEqual(0, timezone.TimeToNextEndTime(thursday).Days);
Assert.AreEqual(8, timezone.TimeToNextEndTime(thursday).Hours);
Assert.AreEqual(0, timezone.TimeToNextEndTime(thursday).Minutes);
Assert.AreEqual("8:hr 0:min 0:sec", timezone.TimeToNextEndTime(thursday).ToCustomString());
DateTime friday = new(2025, 9, 26, 8, 30, 0);
Assert.AreEqual(2, timezone.TimeToNextBeginTime(friday).Days);
Assert.AreEqual(23, timezone.TimeToNextBeginTime(friday).Hours);
Assert.AreEqual(30, timezone.TimeToNextBeginTime(friday).Minutes);
Assert.AreEqual("2:days 23:hr 30:min 0:sec", timezone.TimeToNextBeginTime(friday).ToCustomString());
Assert.AreEqual(0, timezone.TimeToNextEndTime(friday).Days);
Assert.AreEqual(8, timezone.TimeToNextEndTime(friday).Hours);
Assert.AreEqual(0, timezone.TimeToNextEndTime(friday).Minutes);
Assert.AreEqual("8:hr 0:min 0:sec", timezone.TimeToNextEndTime(friday).ToCustomString());
DateTime saturday = new(2025, 9, 27, 8, 30, 0);
Assert.AreEqual(1, timezone.TimeToNextBeginTime(saturday).Days);
Assert.AreEqual(23, timezone.TimeToNextBeginTime(saturday).Hours);
Assert.AreEqual(30, timezone.TimeToNextBeginTime(saturday).Minutes);
Assert.AreEqual("1:days 23:hr 30:min 0:sec", timezone.TimeToNextBeginTime(saturday).ToCustomString());
Assert.AreEqual(2, timezone.TimeToNextEndTime(saturday).Days);
Assert.AreEqual(8, timezone.TimeToNextEndTime(saturday).Hours);
Assert.AreEqual(0, timezone.TimeToNextEndTime(saturday).Minutes);
Assert.AreEqual("2:days 8:hr 0:min 0:sec", timezone.TimeToNextEndTime(saturday).ToCustomString());
DateTime sunday = new(2025, 9, 28, 8, 30, 0);
Assert.AreEqual(0, timezone.TimeToNextBeginTime(sunday).Days);
Assert.AreEqual(23, timezone.TimeToNextBeginTime(sunday).Hours);
Assert.AreEqual(30, timezone.TimeToNextBeginTime(sunday).Minutes);
Assert.AreEqual("23:hr 30:min 0:sec", timezone.TimeToNextBeginTime(sunday).ToCustomString());
Assert.AreEqual(1, timezone.TimeToNextEndTime(sunday).Days);
Assert.AreEqual(8, timezone.TimeToNextEndTime(sunday).Hours);
Assert.AreEqual(0, timezone.TimeToNextEndTime(sunday).Minutes);
Assert.AreEqual("1:days 8:hr 0:min 0:sec", timezone.TimeToNextEndTime(sunday).ToCustomString());
}
}
}

View File

@@ -31,6 +31,7 @@ namespace Aperio_Control_Centre.ACSDatabase
public DbSet<Models.AuthorizationEntry> AuthorizationMatrix { get; set; } = null!;
public DbSet<Models.DoorModeSchedule> DoorModeSchedules { get; set; } = null!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
@@ -102,8 +103,18 @@ namespace Aperio_Control_Centre.ACSDatabase
.HasColumnName("Info")
.HasMaxLength(500);
entity.OwnsMany<SupportedDoorMode>(
mode => mode.DoorModes, builder => { builder.ToJson(); });
entity.OwnsMany<SupportedDoorMode>(e => e.DoorModes, b =>
{
b.ToJson();
});
//entity.OwnsMany<DoorModeSchedulerItem>(e => e.DoorModeScheduler, b => { b.ToJson().Ignore(x => x.TimeZone); });
//entity.OwnsMany<DoorModeSchedulerItem>(e => e.DoorModeScheduler, b =>
//{
// b.ToJson();
// //b.Ignore(x => x.TimeZone);
// //b.OwnsOne(t => t.Timezone);
//});
entity.Property(e => e.ProductClass)
.HasColumnName("ProductClass");
@@ -490,7 +501,7 @@ namespace Aperio_Control_Centre.ACSDatabase
entity.Ignore(e => e.WholeDayTime);
entity.Ignore(e => e.GetTimezoneString);
entity.Ignore(e => e.GetWeekdayShortString);
entity.Ignore(e => e.IsTimezoneActive2);
//entity.Ignore(e => e.IsTimezoneActive2);
entity.HasMany(e => e.Authorizations)
.WithOne(e => e.Timezone)
@@ -665,6 +676,24 @@ namespace Aperio_Control_Centre.ACSDatabase
// .IsRequired();
});
modelBuilder.Entity<DoorModeSchedule>(entity =>
{
entity.ToTable("DoorModeSchedules");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).HasColumnName("ID");
entity.Property(e => e.DeviceId).HasColumnName("Device_Id").IsRequired(true);
entity.Property(e => e.DoorMode).HasColumnName("DoorMode").IsRequired(true);
entity.Property(e => e.TimezoneId).HasColumnName("Timezone_Id").IsRequired(true);
entity.HasOne(d => d.Device)
.WithMany(p => p.DoorModeSchedules)
.HasForeignKey(d => d.DeviceId);
entity.HasOne(d => d.Timezone)
.WithMany(p => p.DoorModeSchedules)
.HasForeignKey(d => d.TimezoneId);
});
}
}
}

View File

@@ -0,0 +1,944 @@
// <auto-generated />
using System;
using Aperio_Control_Centre.ACSDatabase;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Aperio_Control_Centre.ACSDatabase.Migrations
{
[DbContext(typeof(ACSDatabaseContext))]
[Migration("20250919125957_[AddDoorModeScheduling]")]
partial class AddDoorModeScheduling
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.AuthorizationEntry", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("DeviceGroupId")
.HasColumnType("int")
.HasColumnName("DeviceGroup_Id");
b.Property<int>("TimezoneId")
.HasColumnType("int")
.HasColumnName("Timezone_Id");
b.Property<int>("UserGroupId")
.HasColumnType("int")
.HasColumnName("UserGroup_Id");
b.HasKey("Id");
b.HasIndex("DeviceGroupId");
b.HasIndex("TimezoneId");
b.HasIndex("UserGroupId");
b.ToTable("AuthorizationMatrix", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Department", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("Active")
.HasColumnType("int")
.HasColumnName("Active");
b.Property<string>("Info")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)")
.HasColumnName("Info");
b.Property<DateTime>("LastChanged")
.HasColumnType("datetime")
.HasColumnName("LastChanged");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("Name");
b.HasKey("Id");
b.ToTable("Departments", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Device", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("Active")
.HasColumnType("int")
.HasColumnName("Active");
b.Property<int?>("BatteryState")
.HasColumnType("int")
.HasColumnName("BatteryState");
b.Property<int?>("ConnectionState")
.HasColumnType("int")
.HasColumnName("ConnectionState");
b.Property<string>("DeviceId")
.IsRequired()
.HasMaxLength(10)
.HasColumnType("nvarchar(10)")
.HasColumnName("DeviceID");
b.Property<int?>("DoorState")
.HasColumnType("int")
.HasColumnName("DoorState");
b.Property<int?>("EmergencyInsideState")
.HasColumnType("int")
.HasColumnName("EmergencyInsideState");
b.Property<int?>("EmergencyOutsideState")
.HasColumnType("int")
.HasColumnName("EmergencyOutsideState");
b.Property<int?>("HandleState")
.HasColumnType("int")
.HasColumnName("HandleState");
b.Property<string>("HubId")
.HasMaxLength(10)
.HasColumnType("nvarchar(10)")
.HasColumnName("HubID");
b.Property<string>("Info")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)")
.HasColumnName("Info");
b.Property<string>("Ipaddress")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("Ipaddress");
b.Property<int?>("KeyCylinderState")
.HasColumnType("int")
.HasColumnName("KeyCylinderState");
b.Property<DateTime>("LastChanged")
.HasColumnType("datetime")
.HasColumnName("LastChanged");
b.Property<DateTime?>("LastData")
.HasColumnType("datetime")
.HasColumnName("LastData");
b.Property<int?>("LocationId")
.HasColumnType("int")
.HasColumnName("Location_Id");
b.Property<int?>("LockState")
.HasColumnType("int")
.HasColumnName("LockState");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("Name");
b.Property<int?>("ProductClass")
.HasColumnType("int")
.HasColumnName("ProductClass");
b.Property<int?>("TamperState")
.HasColumnType("int")
.HasColumnName("TamperState");
b.Property<int?>("UnlockTime")
.HasColumnType("int")
.HasColumnName("UnlockTime");
b.Property<int?>("UnlockTimezoneId")
.HasColumnType("int")
.HasColumnName("UnlockTimezone_Id");
b.HasKey("Id");
b.HasIndex(new[] { "LocationId" }, "IX_FK_Devices_Locations");
b.HasIndex(new[] { "UnlockTimezoneId" }, "IX_FK_Devices_Timezones");
b.HasIndex(new[] { "DeviceId" }, "NonClusteredIndex-Device_Id");
b.ToTable("Devices", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.DeviceGroup", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("Active")
.HasColumnType("int")
.HasColumnName("Active");
b.Property<string>("Info")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)")
.HasColumnName("Info");
b.Property<DateTime>("LastChanged")
.HasColumnType("datetime")
.HasColumnName("LastChanged");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("Name");
b.HasKey("Id");
b.ToTable("DeviceGroups", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.DeviceGroupJoin", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("DeviceId")
.HasColumnType("int")
.HasColumnName("Device_Id");
b.Property<int>("GroupId")
.HasColumnType("int")
.HasColumnName("Group_Id");
b.HasKey("Id");
b.HasIndex(new[] { "DeviceId" }, "IX_FK_DeviceGroupJoins_Device");
b.HasIndex(new[] { "GroupId" }, "IX_FK_DeviceGroupJoins_Group");
b.ToTable("DeviceGroupJoins");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.DeviceGroupJoin2", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("DeviceGroupId")
.HasColumnType("int")
.HasColumnName("DeviceGroup_Id");
b.Property<int>("DeviceId")
.HasColumnType("int")
.HasColumnName("Device_Id");
b.HasKey("Id");
b.HasIndex("DeviceGroupId");
b.HasIndex("DeviceId");
b.ToTable("DeviceGroupJoins2", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.DoorModeSchedule", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("DeviceId")
.HasColumnType("int")
.HasColumnName("Device_Id");
b.Property<int>("DoorMode")
.HasColumnType("int")
.HasColumnName("DoorMode");
b.Property<int>("ExecuteMode")
.HasColumnType("int");
b.Property<int>("TimezoneId")
.HasColumnType("int")
.HasColumnName("Timezone_Id");
b.HasKey("Id");
b.HasIndex("DeviceId");
b.HasIndex("TimezoneId");
b.ToTable("DoorModeSchedules", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.GlobalSetting", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("Name");
b.Property<string>("Value")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)")
.HasColumnName("Value");
b.HasKey("Id");
b.ToTable("GlobalSettings", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Group", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("Active")
.HasColumnType("int")
.HasColumnName("Active");
b.Property<string>("Info")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)")
.HasColumnName("Info");
b.Property<DateTime>("LastChanged")
.HasColumnType("datetime")
.HasColumnName("LastChanged");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("Name");
b.Property<int>("TimezoneId")
.HasColumnType("int")
.HasColumnName("Timezone_Id");
b.HasKey("Id");
b.HasIndex("TimezoneId");
b.ToTable("Groups", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Location", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("Active")
.HasColumnType("int")
.HasColumnName("Active");
b.Property<string>("City")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("City");
b.Property<string>("Country")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("Country");
b.Property<string>("Info")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)")
.HasColumnName("Info");
b.Property<DateTime>("LastChanged")
.HasColumnType("datetime")
.HasColumnName("LastChanged");
b.Property<string>("Latitude")
.HasMaxLength(12)
.HasColumnType("nvarchar(12)")
.HasColumnName("Latitude");
b.Property<string>("Longitude")
.HasMaxLength(12)
.HasColumnType("nvarchar(12)")
.HasColumnName("Longitude");
b.Property<string>("Nr")
.HasMaxLength(10)
.HasColumnType("nvarchar(10)")
.HasColumnName("Nr");
b.Property<string>("Province")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("Province");
b.Property<string>("Street")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("Street");
b.Property<string>("Zipcode")
.HasMaxLength(10)
.HasColumnType("nvarchar(10)")
.HasColumnName("Zipcode");
b.HasKey("Id");
b.ToTable("Locations", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Logging", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int?>("Access")
.HasColumnType("int")
.HasColumnName("Access");
b.Property<string>("CredentialString")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("CredentialString");
b.Property<int?>("CredentialType")
.HasColumnType("int")
.HasColumnName("CredentialType");
b.Property<int?>("DeviceId")
.HasColumnType("int")
.HasColumnName("Device_Id");
b.Property<int>("LogType")
.HasColumnType("int")
.HasColumnName("LogType");
b.Property<string>("Message")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)")
.HasColumnName("Message");
b.Property<DateTime>("Time")
.HasColumnType("datetime")
.HasColumnName("Time");
b.Property<int?>("UserId")
.HasColumnType("int")
.HasColumnName("User_Id");
b.Property<string>("UserName")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("UserName");
b.HasKey("Id");
b.HasIndex(new[] { "DeviceId" }, "IX_FK_DevicesLoggingsX");
b.HasIndex(new[] { "UserId" }, "IX_FK_UsersLoggingsX");
b.HasIndex(new[] { "DeviceId" }, "NonClusteredIndex-Device_Id");
b.HasIndex(new[] { "LogType" }, "NonClusteredIndex-LogType");
b.HasIndex(new[] { "UserId" }, "NonClusteredIndex-User_Id");
b.ToTable("Loggings", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Timezone", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("Active")
.HasColumnType("int")
.HasColumnName("Active");
b.Property<TimeSpan>("BeginTime")
.HasColumnType("time")
.HasColumnName("BeginTime");
b.Property<TimeSpan>("EndTime")
.HasColumnType("time")
.HasColumnName("EndTime");
b.Property<string>("Info")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)")
.HasColumnName("Info");
b.Property<DateTime>("LastChanged")
.HasColumnType("datetime")
.HasColumnName("LastChanged");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("Name");
b.Property<int>("TwentyFourHours")
.HasColumnType("int")
.HasColumnName("TwentyFourHours");
b.Property<int>("WeekDays")
.HasColumnType("int")
.HasColumnName("WeekDays");
b.HasKey("Id");
b.ToTable("Timezones", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("Active")
.HasColumnType("int")
.HasColumnName("Active");
b.Property<DateTime>("Created")
.HasColumnType("datetime")
.HasColumnName("Created");
b.Property<string>("CredentialString")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("CredentialString");
b.Property<int>("CredentialType")
.HasColumnType("int")
.HasColumnName("CredentialType");
b.Property<int?>("DepartmentId")
.HasColumnType("int")
.HasColumnName("Department_Id");
b.Property<bool?>("Dordrecht")
.HasColumnType("bit")
.HasColumnName("Dordrecht");
b.Property<int?>("Expires")
.HasColumnType("int")
.HasColumnName("Expires");
b.Property<int?>("GroupId")
.HasColumnType("int")
.HasColumnName("Group_Id");
b.Property<string>("Info")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)")
.HasColumnName("Info");
b.Property<DateTime>("LastChanged")
.HasColumnType("datetime")
.HasColumnName("LastChanged");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("Name");
b.Property<int?>("PassNumber")
.HasColumnType("int")
.HasColumnName("PassNumber");
b.Property<string>("SubLine")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("SubLine");
b.Property<int?>("Watched")
.HasColumnType("int")
.HasColumnName("Watched");
b.HasKey("Id");
b.HasIndex(new[] { "DepartmentId" }, "IX_FK_Users_Departments");
b.HasIndex(new[] { "GroupId" }, "IX_FK_Users_Groups");
b.HasIndex(new[] { "CredentialString" }, "NonClusteredIndex-CredentialString");
b.ToTable("Users", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.UserGroup", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("Active")
.HasColumnType("int")
.HasColumnName("Active");
b.Property<string>("Info")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)")
.HasColumnName("Info");
b.Property<DateTime>("LastChanged")
.HasColumnType("datetime")
.HasColumnName("LastChanged");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("Name");
b.HasKey("Id");
b.ToTable("UserGroups", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.UserGroupJoin", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("UserGroupId")
.HasColumnType("int")
.HasColumnName("UserGroup_Id");
b.Property<int>("UserId")
.HasColumnType("int")
.HasColumnName("User_Id");
b.HasKey("Id");
b.HasIndex("UserGroupId");
b.HasIndex("UserId");
b.ToTable("UserGroupJoins", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.AuthorizationEntry", b =>
{
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.DeviceGroup", "DeviceGroup")
.WithMany("Authorizations")
.HasForeignKey("DeviceGroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Timezone", "Timezone")
.WithMany("Authorizations")
.HasForeignKey("TimezoneId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.UserGroup", "UserGroup")
.WithMany("Authorizations")
.HasForeignKey("UserGroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("DeviceGroup");
b.Navigation("Timezone");
b.Navigation("UserGroup");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Device", b =>
{
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Location", "Location")
.WithMany("Devices")
.HasForeignKey("LocationId")
.HasConstraintName("FK_Devices_Locations");
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Timezone", "UnlockTimezone")
.WithMany("Devices")
.HasForeignKey("UnlockTimezoneId")
.HasConstraintName("FK_Devices_Timezones");
b.OwnsMany("Aperio_Control_Centre.ACSDatabase.Models.SupportedDoorMode", "DoorModes", b1 =>
{
b1.Property<int>("DeviceId")
.HasColumnType("int");
b1.Property<int>("__synthesizedOrdinal")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b1.Property<bool>("CurrentMode")
.HasColumnType("bit");
b1.Property<int>("DoorMode")
.HasColumnType("int");
b1.HasKey("DeviceId", "__synthesizedOrdinal");
b1.ToTable("Devices");
b1.ToJson("DoorModes");
b1.WithOwner()
.HasForeignKey("DeviceId");
});
b.Navigation("DoorModes");
b.Navigation("Location");
b.Navigation("UnlockTimezone");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.DeviceGroupJoin", b =>
{
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Device", "Device")
.WithMany("DeviceGroupJoins")
.HasForeignKey("DeviceId")
.IsRequired()
.HasConstraintName("FK_DeviceGroupJoins_Device");
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Group", "Group")
.WithMany("DeviceGroupJoins")
.HasForeignKey("GroupId")
.IsRequired()
.HasConstraintName("FK_DeviceGroupJoins_Group");
b.Navigation("Device");
b.Navigation("Group");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.DeviceGroupJoin2", b =>
{
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.DeviceGroup", null)
.WithMany()
.HasForeignKey("DeviceGroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Device", null)
.WithMany()
.HasForeignKey("DeviceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.DoorModeSchedule", b =>
{
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Device", "Device")
.WithMany("DoorModeSchedules")
.HasForeignKey("DeviceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Timezone", "Timezone")
.WithMany("DoorModeSchedules")
.HasForeignKey("TimezoneId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Device");
b.Navigation("Timezone");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Group", b =>
{
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Timezone", "Timezone")
.WithMany("Groups")
.HasForeignKey("TimezoneId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_Timezones_Groups");
b.Navigation("Timezone");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Logging", b =>
{
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Device", "Device")
.WithMany("Loggings")
.HasForeignKey("DeviceId")
.HasConstraintName("FK_DevicesLoggingsX");
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.User", "User")
.WithMany("Loggings")
.HasForeignKey("UserId")
.HasConstraintName("FK_UsersLoggingsX");
b.Navigation("Device");
b.Navigation("User");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.User", b =>
{
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Department", "Department")
.WithMany("Users")
.HasForeignKey("DepartmentId")
.HasConstraintName("FK_Users_Departments");
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Group", "Group")
.WithMany("Users")
.HasForeignKey("GroupId")
.HasConstraintName("FK_Users_Groups");
b.Navigation("Department");
b.Navigation("Group");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.UserGroupJoin", b =>
{
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.UserGroup", null)
.WithMany()
.HasForeignKey("UserGroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Department", b =>
{
b.Navigation("Users");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Device", b =>
{
b.Navigation("DeviceGroupJoins");
b.Navigation("DoorModeSchedules");
b.Navigation("Loggings");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.DeviceGroup", b =>
{
b.Navigation("Authorizations");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Group", b =>
{
b.Navigation("DeviceGroupJoins");
b.Navigation("Users");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Location", b =>
{
b.Navigation("Devices");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Timezone", b =>
{
b.Navigation("Authorizations");
b.Navigation("Devices");
b.Navigation("DoorModeSchedules");
b.Navigation("Groups");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.User", b =>
{
b.Navigation("Loggings");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.UserGroup", b =>
{
b.Navigation("Authorizations");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,59 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Aperio_Control_Centre.ACSDatabase.Migrations
{
/// <inheritdoc />
public partial class AddDoorModeScheduling : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "DoorModeSchedules",
columns: table => new
{
ID = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Device_Id = table.Column<int>(type: "int", nullable: false),
DoorMode = table.Column<int>(type: "int", nullable: false),
ExecuteMode = table.Column<int>(type: "int", nullable: false),
Timezone_Id = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_DoorModeSchedules", x => x.ID);
table.ForeignKey(
name: "FK_DoorModeSchedules_Devices_Device_Id",
column: x => x.Device_Id,
principalTable: "Devices",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_DoorModeSchedules_Timezones_Timezone_Id",
column: x => x.Timezone_Id,
principalTable: "Timezones",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_DoorModeSchedules_Device_Id",
table: "DoorModeSchedules",
column: "Device_Id");
migrationBuilder.CreateIndex(
name: "IX_DoorModeSchedules_Timezone_Id",
table: "DoorModeSchedules",
column: "Timezone_Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "DoorModeSchedules");
}
}
}

View File

@@ -17,7 +17,7 @@ namespace Aperio_Control_Centre.ACSDatabase.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.4")
.HasAnnotation("ProductVersion", "9.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
@@ -283,6 +283,39 @@ namespace Aperio_Control_Centre.ACSDatabase.Migrations
b.ToTable("DeviceGroupJoins2", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.DoorModeSchedule", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("DeviceId")
.HasColumnType("int")
.HasColumnName("Device_Id");
b.Property<int>("DoorMode")
.HasColumnType("int")
.HasColumnName("DoorMode");
b.Property<int>("ExecuteMode")
.HasColumnType("int");
b.Property<int>("TimezoneId")
.HasColumnType("int")
.HasColumnName("Timezone_Id");
b.HasKey("Id");
b.HasIndex("DeviceId");
b.HasIndex("TimezoneId");
b.ToTable("DoorModeSchedules", (string)null);
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.GlobalSetting", b =>
{
b.Property<int>("Id")
@@ -771,6 +804,25 @@ namespace Aperio_Control_Centre.ACSDatabase.Migrations
.IsRequired();
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.DoorModeSchedule", b =>
{
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Device", "Device")
.WithMany("DoorModeSchedules")
.HasForeignKey("DeviceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Timezone", "Timezone")
.WithMany("DoorModeSchedules")
.HasForeignKey("TimezoneId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Device");
b.Navigation("Timezone");
});
modelBuilder.Entity("Aperio_Control_Centre.ACSDatabase.Models.Group", b =>
{
b.HasOne("Aperio_Control_Centre.ACSDatabase.Models.Timezone", "Timezone")
@@ -841,6 +893,8 @@ namespace Aperio_Control_Centre.ACSDatabase.Migrations
{
b.Navigation("DeviceGroupJoins");
b.Navigation("DoorModeSchedules");
b.Navigation("Loggings");
});
@@ -867,6 +921,8 @@ namespace Aperio_Control_Centre.ACSDatabase.Migrations
b.Navigation("Devices");
b.Navigation("DoorModeSchedules");
b.Navigation("Groups");
});

View File

@@ -40,9 +40,9 @@ namespace Aperio_Control_Centre.ACSDatabase.Models
public string? Info { get; set; }
[Display(Name = nameof(DoorModes))]
//public List<SupportedDoorMode>? DoorModes { get; set; }
public SupportedDoorModes? DoorModes { get; set; }
[Display(Name = nameof(ProductClass))]
public ProductClass? ProductClass { get; set; }
@@ -77,6 +77,7 @@ namespace Aperio_Control_Centre.ACSDatabase.Models
[Range(0, int.MaxValue, ErrorMessage = "Seconden groter dan 0")]
public int? UnlockTime { get; set; }
[Obsolete("Use door mode schedules")]
[Display(Name = nameof(UnlockTimezone))]
public int? UnlockTimezoneId { get; set; }
@@ -92,7 +93,7 @@ namespace Aperio_Control_Centre.ACSDatabase.Models
[DisplayFormat(DataFormatString = "{0:dd-MM-yyyy HH:mm:ss}", ApplyFormatInEditMode = true)]
public DateTime LastChanged { get; set; }
//todo use last data
//TODO: use last data
[Display(Name = nameof(LastData))]
[DisplayFormat(DataFormatString = "{0:dd-MM-yyyy HH:mm:ss}", ApplyFormatInEditMode = true)]
public DateTime? LastData { get; set; }
@@ -101,6 +102,7 @@ namespace Aperio_Control_Centre.ACSDatabase.Models
[Display(Name = nameof(Location))]
public Location? Location { get; set; }
[Obsolete("Use door mode schedules")]
[Display(Name = nameof(UnlockTimezone))]
public Timezone? UnlockTimezone { get; set; }
@@ -113,6 +115,13 @@ namespace Aperio_Control_Centre.ACSDatabase.Models
public List<DeviceGroup> DeviceGroups { get; } = [];
[Display(Name = nameof(DoorModeSchedules))]
public List<DoorModeSchedule>? DoorModeSchedules { get; set; }
[NotMapped]
public SelectList? LocationSelection { get; set; }
//public Device? Gateway { get; set; }
//public ICollection<Device> ChildDevices { get; } = [];
@@ -122,8 +131,8 @@ namespace Aperio_Control_Centre.ACSDatabase.Models
//[DisplayFormat(DataFormatString = "{0:dd-MM-yyyy HH:mm:ss}", ApplyFormatInEditMode = true)]
//public DateTimeOffset LastChangedLocal => LastChanged.ToLocalTime();
[NotMapped]
public SelectList? LocationSelection { get; set; }
//[NotMapped]
//public SelectList? ExecuteModeSelection { get; set; }
[NotMapped]
[Display(Name = nameof(DeviceGroups))]
@@ -138,7 +147,15 @@ namespace Aperio_Control_Centre.ACSDatabase.Models
[NotMapped]
public List<Logging> LastLoggings { get; set; } = [];
//[NotMapped]
//[Display(Name = "Deur stand planner")]
//public DoorModeScheduler? DoorModeScheduler { get; set; }
public uint OpenTimeDeciseconds()
{

View File

@@ -0,0 +1,30 @@
using Aperio_Control_Centre.Aadp.Enumerations;
using System.ComponentModel.DataAnnotations;
namespace Aperio_Control_Centre.ACSDatabase.Models
{
public class DoorModeSchedule
{
[Key]
public int Id { get; set; }
[Required]
public int DeviceId { get; set; }
[Required]
public DoorMode DoorMode { get; set; }
[Required]
public ExecuteMode ExecuteMode { get; set; }
[Required]
public int TimezoneId { get; set; }
[Display(Name = nameof(Device))]
public Device? Device { get; set; }
[Display(Name = nameof(Timezone))]
public Timezone? Timezone { get; set; }
}
}

View File

@@ -0,0 +1,29 @@
using System.Diagnostics;
namespace Aperio_Control_Centre.ACSDatabase.Models.Extensions
{
public static class DoorModeScheduleExtension
{
public static DoorModeSchedule? GetFirstActiveDoorModeschedule(this List<DoorModeSchedule>? doorModeSchedules)
{
if(doorModeSchedules != null)
{
Debug.WriteLine("has DoorModeSchedules");
foreach (DoorModeSchedule doorModeSchedule in doorModeSchedules)
{
Debug.WriteLine($"DoorModeSchedule - {doorModeSchedule.Timezone?.GetTimezoneString} - {doorModeSchedule.DoorMode} - {doorModeSchedule.ExecuteMode}");
if (doorModeSchedule.Timezone.IsTimezoneActive())
{
Debug.WriteLine("time zone is active");
//await WriteToDevice(new SetDoorModeCommand(_deviceId.ToId(), doorModeSchedule.DoorMode, doorModeSchedule.Timezone.TimeToNextEndTime().ToDeciSeconds(), TimingMode.TIME, doorModeSchedule.ExecuteMode), token);
return doorModeSchedule;
}
}
}
return null;
}
}
}

View File

@@ -8,6 +8,38 @@ namespace Aperio_Control_Centre.ACSDatabase.Models.Extensions
{
public static class LoggingExtensions
{
//public static async Task AddLogAsync(this DbSet<Logging> loggings, ILogEntryBase log, CancellationToken token)
//{
// ArgumentNullException.ThrowIfNull(loggings);
// ArgumentNullException.ThrowIfNull(log);
// Logging logEntry = new()
// {
// Time = DateTime.UtcNow,
// LogType = log.LogType,
// //Device = device,
// //User = user,
// //Access = access,
// //CredentialString = user?.CredentialString,
// //CredentialType = user?.CredentialType,
// Message = log.Message,
// };
// if(log is UserLogEntry userlog)
// {
// logEntry.User = userlog.User;
// }
// await loggings.AddAsync(logEntry, token);
//}
//-----------------------------------
public static async Task AddLogAsync<T>(this DbSet<Logging> loggings, Device device, T state, CancellationToken token) where T : Enum
{
ArgumentNullException.ThrowIfNull(device);

View File

@@ -13,7 +13,7 @@ namespace Aperio_Control_Centre.ACSDatabase.Models.Extensions
.Select(s => new SelectListItem
{
Value = s.Id.ToString(),
Text = s.Name,
Text = $"{s.Name} - {s.GetTimezoneString}",
}).ToListAsync(token);
}

View File

@@ -0,0 +1,9 @@
//using Aperio_Control_Centre.ACSDatabase.Types;
//namespace Aperio_Control_Centre.ACSDatabase.Models.LogEntrys
//{
// public class BootlogEntry : LogEntryBase
// {
// public override LogTypes LogType { get; } = LogTypes.Bootlog;
// }
//}

View File

@@ -0,0 +1,26 @@
////using Aperio_Control_Centre.ACSDatabase.Types;
////namespace Aperio_Control_Centre.ACSDatabase.Models.LogEntrys
////{
//// public class DTCEntry : LogEntryBase
//// {
//// public override LogTypes LogType { get; } = LogTypes.DTC;
//// }
////}
//namespace Aperio_Control_Centre.ACSDatabase.Models.LogEntrys
//{
// public class DTCEntry : Logging
// {
// public DTCEntry()
// {
// Time = DateTime.UtcNow;
// LogType = Types.LogTypes.DTC;
// }
// //public override LogTypes LogType { get; } = LogTypes.DTC;
// }
//}

View File

@@ -0,0 +1,9 @@
//using Aperio_Control_Centre.ACSDatabase.Types;
//namespace Aperio_Control_Centre.ACSDatabase.Models.LogEntrys
//{
// public class DeviceLogEntry : LogEntryBase
// {
// public override LogTypes LogType { get; } = LogTypes.Device;
// }
//}

View File

@@ -0,0 +1,11 @@
//using Aperio_Control_Centre.ACSDatabase.Types;
//namespace Aperio_Control_Centre.ACSDatabase.Models.LogEntrys
//{
// public interface ILogEntryBase
// {
// //public LogTypes LogType { get; }
// //public string? Message { get; set; }
// }
//}

View File

@@ -0,0 +1,10 @@
//using Aperio_Control_Centre.ACSDatabase.Types;
//namespace Aperio_Control_Centre.ACSDatabase.Models.LogEntrys
//{
// public class LogEntryBase : Logging, ILogEntryBase
// {
// public virtual LogTypes LogType { get; } = LogTypes.Unknown;
// public string? Message { get; set; }
// }
//}

View File

@@ -0,0 +1,9 @@
//using Aperio_Control_Centre.ACSDatabase.Types;
//namespace Aperio_Control_Centre.ACSDatabase.Models.LogEntrys
//{
// public class ManualOpenEntry : LogEntryBase
// {
// public override LogTypes LogType { get; } = LogTypes.ManualOpen;
// }
//}

View File

@@ -0,0 +1,9 @@
//using Aperio_Control_Centre.ACSDatabase.Types;
//namespace Aperio_Control_Centre.ACSDatabase.Models.LogEntrys
//{
// public class SystemLogEntry : LogEntryBase
// {
// public override LogTypes LogType { get; } = LogTypes.System;
// }
//}

View File

@@ -0,0 +1,9 @@
//using Aperio_Control_Centre.ACSDatabase.Types;
//namespace Aperio_Control_Centre.ACSDatabase.Models.LogEntrys
//{
// public class UnknownUserEntry : LogEntryBase
// {
// public override LogTypes LogType { get; } = LogTypes.UnknownUser;
// }
//}

View File

@@ -0,0 +1,14 @@
//using Aperio_Control_Centre.ACSDatabase.Types;
//namespace Aperio_Control_Centre.ACSDatabase.Models.LogEntrys
//{
// public class UserLogEntry : LogEntryBase
// {
// public override LogTypes LogType { get; } = LogTypes.User;
// public User? User { get; set; }
// }
//}

View File

@@ -1,4 +1,5 @@
using Aperio_Control_Centre.Aadp.Enumerations;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Aperio_Control_Centre.ACSDatabase.Models
{
@@ -56,5 +57,24 @@ namespace Aperio_Control_Centre.ACSDatabase.Models
}
}
public List<SelectListItem> DoorModeSelectListItems()
{
return this
.Select(s => new SelectListItem
{
Value = s.DoorMode.ToString(),
Text = s.DoorMode.ToString(),
}).ToList();
}
public SelectList DoorModeSelectList()
{
return new SelectList(DoorModeSelectListItems(), "Value", "Text");
}
}
}

View File

@@ -58,6 +58,10 @@ namespace Aperio_Control_Centre.ACSDatabase.Models
[Display(Name = nameof(Authorizations))]
public ICollection<AuthorizationEntry> Authorizations { get; } = [];
[Display(Name = nameof(DoorModeSchedules))]
public ICollection<DoorModeSchedule> DoorModeSchedules { get; } = [];
//[NotMapped]
//[Display(Name = "Last changed")]
//[DisplayFormat(DataFormatString = "{0:dd-MM-yyyy HH:mm:ss}", ApplyFormatInEditMode = true)]
@@ -285,9 +289,9 @@ namespace Aperio_Control_Centre.ACSDatabase.Models
}
[NotMapped]
[Display(Name = "Tijdzone actief")]
public bool IsTimezoneActive2 => IsTimezoneActive(DateTime.Now);
//[NotMapped]
//[Display(Name = "Tijdzone actief")]
//public bool IsTimezoneActive2 => IsTimezoneActive(DateTime.Now);
//[Display(Name = "Tijdzone actief")]
public bool IsTimezoneActive()
@@ -341,5 +345,101 @@ namespace Aperio_Control_Centre.ACSDatabase.Models
}
return true;
}
public TimeSpan TimeToNextBeginTime()
{
return TimeToNextTime(BeginTime, DateTime.Now);
}
public TimeSpan TimeToNextBeginTime(DateTime dateTime)
{
return TimeToNextTime(BeginTime, dateTime);
}
public TimeSpan TimeToNextEndTime()
{
return TimeToNextTime(EndTime, DateTime.Now);
}
public TimeSpan TimeToNextEndTime(DateTime dateTime)
{
return TimeToNextTime(EndTime, dateTime);
}
private TimeSpan TimeToNextTime(TimeSpan time, DateTime currentDateTime)
{
//Debug.WriteLine($"-----------------------------------------------------------");
//Debug.WriteLine($"currentDateTime {currentDateTime.ToString()} - {currentDateTime.DayOfWeek}");
//Debug.WriteLine($"time {time.ToString()}");
if (WeekDays == WeekDaysTypes.Daily)
{
return RemainingTimeOffDay(time, currentDateTime.TimeOfDay);
}
else if (WeekDays == WeekDaysTypes.None)
{
return TimeSpan.Zero;
}
else
{
//Debug.WriteLine($"day of week {today}");
WeekDaysTypes weekday = currentDateTime.DayOfWeek.ToWeekDaysTypes();
if (WeekDays.HasFlag(weekday))
{
//Debug.WriteLine("has flag");
if (time > currentDateTime.TimeOfDay)
{
//on the same day
//Debug.WriteLine($"Same day");
return RemainingTimeOffDay(time, currentDateTime.TimeOfDay);
}
}
IOrderedEnumerable<DayOfWeek> reordereddays = Enum.GetValues<DayOfWeek>()
.Cast<DayOfWeek>()
.OrderBy(d => (d - (currentDateTime.DayOfWeek + 1) + 7) % 7);
TimeSpan days = new();
foreach (DayOfWeek day in reordereddays)
{
//Debug.WriteLine($"----- day = {day}");
WeekDaysTypes? wday = day.ToWeekDaysTypes();
if (WeekDays.HasFlag(wday.Value))
{
//Debug.WriteLine($"wday has flag {wday.Value}");
//Debug.WriteLine($"+ Add day - {days.TotalDays}");
days = days.Add(TimeSpan.FromDays(1));
break;
}
else
{
//Debug.WriteLine($"++ Add day - {days.TotalDays}");
days = days.Add(TimeSpan.FromDays(1));
}
}
//Debug.WriteLine($"Total Days = {days.TotalDays}");
//Debug.WriteLine($"Total Days = {days.Days}");
TimeSpan remaining = time - currentDateTime.TimeOfDay;
//Debug.WriteLine($"Remaining {remaining.TotalSeconds} sec - {remaining.TotalHours} hr");
days = days.Add(remaining);
//Debug.WriteLine($"Days = {days.TotalDays}");
return days;
}
}
private static TimeSpan RemainingTimeOffDay(TimeSpan time, TimeSpan currentTime)
{
TimeSpan remaining = time - currentTime;
//Debug.WriteLine($"Remaining {remaining.TotalSeconds} sec - {remaining.TotalHours} hr");
if (remaining.TotalSeconds < 0)
{
remaining = remaining.Add(TimeSpan.FromDays(1));
}
//Debug.WriteLine($"Remaining +24hr {remaining.TotalSeconds} sec - {remaining.TotalHours} hr");
return remaining;
}
}
}

View File

@@ -189,4 +189,7 @@
<data name="TamperState" xml:space="preserve">
<value>Tamper status</value>
</data>
<data name="DoorModeSchedules" xml:space="preserve">
<value>Deur stand planning</value>
</data>
</root>

View File

@@ -189,4 +189,7 @@
<data name="TamperState" xml:space="preserve">
<value>Tamper status</value>
</data>
<data name="DoorModeSchedules" xml:space="preserve">
<value>Door mode schedules</value>
</data>
</root>

View File

@@ -24,4 +24,23 @@ namespace Aperio_Control_Centre.ACSDatabase.Types
[Display(Name = "Dagelijks")]
Daily = 128
}
public static class WeekDaysTypesExtensions
{
public static WeekDaysTypes ToWeekDaysTypes(this DayOfWeek value)
{
// insert switch statement here
return value switch
{
DayOfWeek.Monday => WeekDaysTypes.Monday,
DayOfWeek.Tuesday => WeekDaysTypes.Tuesday,
DayOfWeek.Wednesday => WeekDaysTypes.Wednesday,
DayOfWeek.Thursday => WeekDaysTypes.Thursday,
DayOfWeek.Friday => WeekDaysTypes.Friday,
DayOfWeek.Saturday => WeekDaysTypes.Saturday,
DayOfWeek.Sunday => WeekDaysTypes.Sunday,
_ => WeekDaysTypes.None,
};
}
}
}

View File

@@ -4,6 +4,9 @@ namespace Aperio_Control_Centre.Aadp.Commands
{
public class CommandIdBase
{
/// <summary>
/// A unique ID for this Device Group
/// </summary>
public Id DeviceGroupId { get; protected set; } = new();
}
}

View File

@@ -4,12 +4,30 @@ using System.Text;
namespace Aperio_Control_Centre.Aadp.Commands.DoorControl
{
/// <summary>
/// SetDoorModeCommand asks the Door (or Device Group) to change its door mode.
/// The door mode is the behavior of the entire door setup; it may affect more than one device.
/// The new door mode can either have a specified duration or be valid until a new SetDoorModeCommand is sent.
/// This is selected by setting the Timing type in the Duration field to TIME and Duration
/// Time to the desired duration, setting Duration Mode to INFINITE or DEFAULT will result in
/// INFINITE duration.If choosing a specific time, the door will change to its most restrictive mode after the specified duration.
/// All modes might not be supported by all doors; the command should be implemented with a best effort approach.
/// </summary>
public class SetDoorModeCommand : CommandIdBase, IPduCommand
{
public Enumerations.PDUTypes PDUType => Enumerations.PDUTypes.SetDoorModeCommand;
/// <summary>
/// Door mode to set.
/// </summary>
public Enumerations.DoorMode Mode { get; set; }
/// <summary>
/// The duration of this door mode.
/// </summary>
public Timing Duration { get; set; } = new();
/// <summary>
/// When the door mode command shall be executed.
/// </summary>
public Enumerations.ExecuteMode Exec { get; set; }
public SetDoorModeCommand() { }
@@ -20,6 +38,13 @@ namespace Aperio_Control_Centre.Aadp.Commands.DoorControl
// Duration = new Timing(time, mode);
// Exec = execute;
//}
//public SetDoorModeCommand(DeviceId deviceId, Enumerations.DoorMode doorMode, uint time, Enumerations.TimingMode mode, Enumerations.ExecuteMode execute)
//{
// DeviceGroupId = deviceId.ToId();
// Mode = doorMode;
// Duration = new Timing(time, mode);
// Exec = execute;
//}
public SetDoorModeCommand(Id id, Enumerations.DoorMode doorMode, uint time, Enumerations.TimingMode mode, Enumerations.ExecuteMode execute)
{
DeviceGroupId = id;
@@ -27,6 +52,13 @@ namespace Aperio_Control_Centre.Aadp.Commands.DoorControl
Duration = new Timing(time, mode);
Exec = execute;
}
public SetDoorModeCommand(Id id, Enumerations.DoorMode doorMode, Enumerations.ExecuteMode execute)
{
DeviceGroupId = id;
Mode = doorMode;
Duration = new Timing(0, Enumerations.TimingMode.INFINITE);
Exec = execute;
}
public void Deserialize(ref byte[] arr, ref int idx)
{

View File

@@ -1,12 +1,33 @@
namespace Aperio_Control_Centre.Aadp.Enumerations
{
/// <summary>
/// Describes if a credential was granted access or not.
/// </summary>
public enum AccessDecision : int
{
/// <summary>
/// Access Denied
/// </summary>
DENIED = 0,
/// <summary>
/// Access Granted
/// </summary>
GRANTED = 1,
/// <summary>
/// Reserved, do not use
/// </summary>
RESERVED = 2,
/// <summary>
/// Reserved, do not use
/// </summary>
RESERVED1 = 3,
/// <summary>
/// Credential was not valid alone (typically a PIN is needed also)
/// </summary>
ANOTHER_CREDENTIAL_REQUESTED = 4,
/// <summary>
/// Access decision is not applicable (no credential data in message)
/// </summary>
NOT_APPLICABLE = 127
}
}

View File

@@ -1,16 +1,51 @@
namespace Aperio_Control_Centre.Aadp.Enumerations
{
/// <summary>
/// Describes what triggered the message CredentialManagementNotification.
/// </summary>
[Flags]
public enum CredentialManagementNotificationType : int
{
/// <summary>
/// Dont use.
/// </summary>
RESERVED = 0x00,
/// <summary>
/// Credential has been added to storage.
/// Response to add credential.
/// </summary>
CREDENTIAL_ADDED = 0x01,
/// <summary>
/// A single credential has been deleted.
/// Response to delete credential.
/// </summary>
SINGLE_CREDENTIAL_DELETED = 0x02,
/// <summary>
/// All credentials have been deleted.
/// Response to delete credential.
/// </summary>
ALL_CREDENTIALS_DELETED = 0x03,
/// <summary>
/// All dynamically added credentials have been deleted.
/// Response to delete credential.
/// </summary>
ALL_DYNAMIC_CREDENTIALS_DELETED = 0x04,
/// <summary>
/// All statically added credentials have been deleted.
/// Response to delete credential.
/// </summary>
ALL_STATIC_CREDENTIALS_DELETED = 0x05,
/// <summary>
/// Initiated by device, packet sequence number is zero.
/// </summary>
CREDENTIAL_DATABASE_UPDATED = 0x06,
/// <summary>
/// Generic failure, no further details.
/// </summary>
FAIL = 0x80,
/// <summary>
/// Device indicates that storage is full.
/// </summary>
FAILED_STORAGE_FULL = 0x81
}
}

View File

@@ -1,10 +1,25 @@
namespace Aperio_Control_Centre.Aadp.Enumerations
{
/// <summary>
/// The DoorSide describes which side of a door a certain state or event is related to.
/// </summary>
public enum DoorSide : int
{
/// <summary>
/// DoorSide is not known or irrelevant.
/// </summary>
UNKNOWN = 0,
/// <summary>
/// DoorSide is on the inside, or the secure side.
/// </summary>
INSIDE = 1,
/// <summary>
/// oorSide is on the outside, or the unsecure side.
/// </summary>
OUTSIDE = 2,
/// <summary>
/// Both sides.
/// </summary>
BOTH = 3
}
}

View File

@@ -1,9 +1,21 @@
namespace Aperio_Control_Centre.Aadp.Enumerations
{
/// <summary>
/// The DoorState describes the state of the door, i.e. if the door is open or closed.
/// </summary>
public enum DoorState : int
{
/// <summary>
/// The state of the door is unknown
/// </summary>
UNKNOWN = 0,
/// <summary>
/// At least one door monitoring sensor reports that the door is open
/// </summary>
OPEN = 1,
/// <summary>
/// All door monitoring sensors report that the door is closed
/// </summary>
CLOSED = 2,
}
}

View File

@@ -1,4 +1,7 @@
namespace Aperio_Control_Centre.Aadp.Enumerations
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Aperio_Control_Centre.Aadp.Enumerations
{
/// <summary>
/// An ExecuteMode defines a command execution mode. The execution mode indicates the
@@ -10,15 +13,18 @@
/// As soon as possible.
/// If the command cannot be executed immediately, it may be done later.
/// </summary>
[Display(Name = "zo snel mogelijk")]
ASAP = 0,
/// <summary>
/// Must be performed immediately after the message is
/// received, it is not allowed to be performed at a later time.
/// </summary>
[Display(Name = "onmiddellijk")]
IMMEDIATE = 1,
/// <summary>
/// Perform at next door opening sequence.
/// </summary>
[Display(Name = "volgende deur beweging")]
DOORSEQUENCE = 2,
}
}
}

View File

@@ -1,25 +1,88 @@
namespace Aperio_Control_Centre.Aadp.Enumerations
{
/// <summary>
/// The Indication identifies a type of indication.
/// These values represent the logical indication, i.e.the message conveyed to the user.
/// They do not specify the behavior in terms of visual and audible devices used to perform the indication.
/// The specific behavior depends on the door equipment.
/// </summary>
public enum Indication : int
{
/// <summary>
/// Enter PIN / use keypad
/// </summary>
KEYPAD = 0,
/// <summary>
/// Show card / use tag
/// </summary>
USE_TAG = 1,
/// <summary>
/// Reserved
/// </summary>
RESERVED1 = 2,
/// <summary>
/// Access Granted
/// </summary>
GRANTED = 3,
/// <summary>
/// Access Denied
/// </summary>
DENIED = 4,
/// <summary>
/// Back light
/// </summary>
BACKLIGHT = 5,
/// <summary>
/// Alarm armed
/// </summary>
ARMED = 6,
/// <summary>
/// Alarm disarmed
/// </summary>
DISARMED = 7,
/// <summary>
/// Application defined 1
/// </summary>
APPL1 = 8,
/// <summary>
/// Application defined 2
/// </summary>
APPL2 = 9,
/// <summary>
/// Application defined 3
/// </summary>
APPL3 = 10,
/// <summary>
/// Door held open for too long
/// </summary>
DOOR_HELD = 11,
/// <summary>
/// Door forced open without being unlocked first
/// </summary>
DOOR_FORCED = 12,
/// <summary>
/// User attention is required by external system
/// </summary>
USER_ATTENTION = 13,
/// <summary>
/// The Fire Alarm has been triggered
/// </summary>
FIRE_ALARM = 14,
/// <summary>
/// Emit a beacon signal for low visibility (smoke) guidance
/// </summary>
BEACON_SIGNAL = 15,
/// <summary>
/// Burglary alarm has been triggered
/// </summary>
BURGLARY_ALARM = 16,
/// <summary>
/// The Panic Alarm has been triggered
/// </summary>
PANIC_ALARM = 17,
/// <summary>
/// Acknowledge input from user
/// </summary>
ACKNOWLEDGE = 18,
}
}

View File

@@ -2,9 +2,20 @@
namespace Aperio_Control_Centre.Aadp.Types.ApplicationTypes
{
/// <summary>
/// The Timing describes different timing constraints
/// </summary>
public class Timing : IPduType
{
/// <summary>
/// Describes how the Time field should be interpreted.
/// </summary>
public Enumerations.TimingMode Mode { get; set; }
/// <summary>
/// The time specified in deciseconds i.e. 10ths of seconds.
/// This value is only relevant if Mode is TIME.
/// The field is either way included in the message.
/// </summary>
public VariableLengthQuantity Time { get; set; } = new();
public Timing() { }

View File

@@ -54,10 +54,9 @@ namespace Aperio_Control_Centre.AperioServer
_connection = connection;
}
public Task RemoveConnection()
public void RemoveConnection()
{
Debug.WriteLine("TestingAperioConnectionContext RemoveConnection");
return Task.CompletedTask;
}
public Task Receive(CancellationTokenSource linkedCts)

View File

@@ -13,7 +13,7 @@ using System.Threading.Tasks;
namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
{
internal class TestingAperioHubDevice : IAperioHubDevice
internal sealed class TestingAperioHubDevice : IAperioHubDevice
{
private IAperioConnectionContext? _connection;
private DeviceId _deviceId;

View File

@@ -6,6 +6,7 @@ using Aperio_Control_Centre.Aadp.Types.ApplicationTypes;
using Aperio_Control_Centre.ACSDatabase.Types;
using Aperio_Control_Centre.AperioServer;
using Aperio_Control_Centre.AperioServer.Devices;
using Aperio_Control_Centre.Models;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -14,7 +15,7 @@ using System.Threading.Tasks;
namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
{
internal class TestingAperioLockDevice : IAperioLockDevice
internal sealed class TestingAperioLockDevice : IAperioLockDevice
{
private IAperioConnectionContext? _connection;
private DeviceId _deviceId;
@@ -56,6 +57,8 @@ namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
public HandleState HandleState => throw new NotImplementedException();
public List<SchedulerItem> DoorModeSchedules => throw new NotImplementedException();
public event EventHandler? DeviceChanged;
public Task ClearDTCOnDevice(uint dtcId, CancellationToken token)
@@ -217,5 +220,15 @@ namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
{
throw new NotImplementedException();
}
public Task LoadDoorModeSchedule(CancellationToken token)
{
throw new NotImplementedException();
}
//public void SetDoorModeOnDevice(DoorMode? doorMode)
//{
// throw new NotImplementedException();
//}
}
}

View File

@@ -6,6 +6,7 @@ using Aperio_Control_Centre.Aadp.Types.ApplicationTypes;
using Aperio_Control_Centre.ACSDatabase.Types;
using Aperio_Control_Centre.AperioServer;
using Aperio_Control_Centre.AperioServer.Devices;
using Aperio_Control_Centre.Models;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -14,7 +15,7 @@ using System.Threading.Tasks;
namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
{
internal class TestingAperioOptaDevice : IAperioOptaDevice
internal sealed class TestingAperioOptaDevice : IAperioOptaDevice
{
private IAperioConnectionContext? _connection;
private DeviceId _deviceId;
@@ -51,6 +52,8 @@ namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
public ActivatorState EmergencyInsideState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public ActivatorState EmergencyOutsideState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public List<SchedulerItem> DoorModeSchedules => throw new NotImplementedException();
public event EventHandler? DeviceChanged;
public Task ClearDTCOnDevice(uint dtcId, CancellationToken token)
@@ -190,5 +193,15 @@ namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
{
throw new NotImplementedException();
}
public Task LoadDoorModeSchedule(CancellationToken token)
{
throw new NotImplementedException();
}
//public void SetDoorModeOnDevice(DoorMode? doorMode)
//{
// throw new NotImplementedException();
//}
}
}

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
{
internal class TestingAperioTimeoutFeature : IAperioTimeoutFeature
internal sealed class TestingAperioTimeoutFeature : IAperioTimeoutFeature
{
public DateTime GetStartTime => DateTime.Now;

View File

@@ -1,13 +1,15 @@
using Aperio_Control_Centre.Aadp.Enumerations;
using Aperio_Control_Centre.Aadp.Types;
using Aperio_Control_Centre.Aadp.Types.ApplicationTypes;
using Aperio_Control_Centre.ACSDatabase.Models;
using Aperio_Control_Centre.AperioServer;
using Aperio_Control_Centre.AperioServer.Devices;
using Aperio_Control_Centre.Models;
using System.Diagnostics;
namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
{
internal class TestingDeviceList : IAperioDeviceList
internal sealed class TestingDeviceList : IAperioDeviceList
{
public event EventHandler DeviceListChanged;
@@ -47,6 +49,11 @@ namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
return [];
}
public IEnumerable<SchedulerItem> GetSchedules()
{
throw new NotImplementedException();
}
public void RemoveConnection(IAperioConnectionContext conn)
{
Debug.WriteLine("RemoveConnection");
@@ -57,5 +64,10 @@ namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
Debug.WriteLine("RemoveDevice");
return Task.CompletedTask;
}
IEnumerable<DoorModeSchedule> IAperioDeviceList.GetSchedules()
{
throw new NotImplementedException();
}
}
}

View File

@@ -2,7 +2,7 @@
namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
{
internal class TestingLogger<T> : ILogger<T>
internal sealed class TestingLogger<T> : ILogger<T>
{
public List<string> Messages { get; } = [];

View File

@@ -3,7 +3,7 @@ using Aperio_Control_Centre.ScreenLogging;
namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
{
internal class TestingScreenLoggerBridge : IScreenLoggerBridge
internal sealed class TestingScreenLoggerBridge : IScreenLoggerBridge
{
public List<string> Messages { get; } = [];

View File

@@ -5,7 +5,7 @@ using System.Diagnostics;
namespace Aperio_Control_Centre.UnitTest.AperioServer.Setups
{
internal class TestingServerInfoService : IServerInfoService
internal sealed class TestingServerInfoService : IServerInfoService
{
public List<string> Messages { get; } = [];

View File

@@ -98,6 +98,7 @@ namespace Aperio_Control_Centre.ACSCredentials
.Include(l => l.Location)
.Include(t => t.UnlockTimezone)
.Include(d => d.DeviceGroups)
.Include(s => s.DoorModeSchedules!).ThenInclude(t => t.Timezone)
.SingleOrDefaultAsync(token);
if (Device == null)

View File

@@ -0,0 +1,53 @@
//using System.Diagnostics;
//using System.Timers;
//namespace Aperio_Control_Centre.AperioServer
//{
// public sealed class AperioAlarmClock : IDisposable
// {
// private System.Timers.Timer alarmTimer;
// public void Dispose()
// {
// //throw new NotImplementedException();
// alarmTimer.Dispose();
// }
// public void SetAlarm(DateTime alarmTime)
// {
// Debug.WriteLine($"Enter the alarm time (HH:mm): {alarmTime}");
// //string inputTime = Console.ReadLine();
// //if (DateTime.TryParse(inputTime, out DateTime alarmTime))
// //{
// //DateTime now = DateTime.Now;
// TimeSpan timeToAlarm = alarmTime - DateTime.Now;
// if (timeToAlarm < TimeSpan.Zero)
// {
// timeToAlarm = timeToAlarm.Add(TimeSpan.FromDays(1)); // Set for the next day
// }
// Debug.WriteLine($"Alarm set for {alarmTime.TimeOfDay}. It will ring in {timeToAlarm.TotalSeconds} seconds.");
// alarmTimer = new System.Timers.Timer(timeToAlarm.TotalMilliseconds);
// alarmTimer.Elapsed += OnAlarmTriggered;
// alarmTimer.AutoReset = false; // Trigger only once
// alarmTimer.Start();
// //}
// //else
// //{
// // Debug.WriteLine("Invalid time format. Please use HH:mm.");
// //}
// //Debug.WriteLine("Press Enter to exit...");
// //Console.ReadLine();
// }
// private void OnAlarmTriggered(object? sender, ElapsedEventArgs e)
// {
// Debug.WriteLine("ALARM! Time to wake up!");
// alarmTimer.Dispose(); // Clean up the timer
// }
// }
//}

View File

@@ -7,6 +7,7 @@ using Aperio_Control_Centre.Aadp.Types;
using Aperio_Control_Centre.Aadp.Types.ApplicationTypes;
using Aperio_Control_Centre.ACSDatabase;
using Aperio_Control_Centre.ACSDatabase.Models;
using Aperio_Control_Centre.ACSDatabase.Models.Extensions;
using Aperio_Control_Centre.ACSDatabase.Types;
using Aperio_Control_Centre.AperioServer.Devices;
using Aperio_Control_Centre.Log;
@@ -20,6 +21,8 @@ using System.Buffers;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net;
using System.Security.Principal;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;
namespace Aperio_Control_Centre.AperioServer
{
@@ -40,7 +43,7 @@ namespace Aperio_Control_Centre.AperioServer
public IDuplexPipe Transport { get => _connection.Transport; set => _connection.Transport = value; }
public IPAddress Address { get => ((IPEndPoint)_connection.RemoteEndPoint!).Address; }
public AperioConnectionContext(
IAperioDeviceList devicesList,
@@ -73,7 +76,7 @@ namespace Aperio_Control_Centre.AperioServer
}, connection);
}
public async Task RemoveConnection()
public void RemoveConnection()
{
_connection.Features.Get<IAperioTimeoutFeature>()?.Dispose();
@@ -166,16 +169,29 @@ namespace Aperio_Control_Centre.AperioServer
}
}
//Instantiate a Singleton of the Semaphore with a value of 1. This means that only 1 thread can be granted access at a time.
private static readonly SemaphoreSlim _writeSemaphoreSlim = new(1, 1);
public async Task WriteToDevice(IPduCommand command, CancellationToken token)
{
await Transport.Output.WriteAsync(PduMessage.Serialize(command), token);
_screenLogger.AddScreenlog(new AperioScreenLog()
//Asynchronously wait to enter the Semaphore. If no-one has been granted access to the Semaphore, code execution will proceed, otherwise this thread waits here until the semaphore is released
await _writeSemaphoreSlim.WaitAsync(token);
try
{
IPAddress = Address,
Direction = LogDirections.TX,
PDUType = command.PDUType,
Message = command.LogString()
});
await Transport.Output.WriteAsync(PduMessage.Serialize(command), token);
_screenLogger.AddScreenlog(new AperioScreenLog()
{
IPAddress = Address,
Direction = LogDirections.TX,
PDUType = command.PDUType,
Message = command.LogString()
});
}
finally
{
//When the task is ready, release the semaphore. It is vital to ALWAYS release the semaphore when we are ready, or else we will end up with a Semaphore that is forever locked.
//This is why it is important to do the Release within a try...finally clause; program execution may crash or take a different path, this way you are guaranteed execution
_writeSemaphoreSlim.Release();
}
}
//---------------------------- Aperio Data handeling ------------------------------------------------------
@@ -234,14 +250,14 @@ namespace Aperio_Control_Centre.AperioServer
case GetDeviceParameterResult command:
foreach (DeviceParameterNotificationBlock block in command.ParameterResultBlock)
{
await DeviceParameterAction(command.Device, block, token);
DeviceParameterAction(command.Device, block);
}
break;
case DeviceParameterNotification command:
foreach (DeviceParameterNotificationBlock block in command.ParameterNotificationBlock)
{
await DeviceParameterAction(command.Device, block, token);
DeviceParameterAction(command.Device, block);
}
break;
@@ -249,28 +265,40 @@ namespace Aperio_Control_Centre.AperioServer
if (pducommand is CommandDeviceIdBase deviceid)
{
Debug.WriteLine($"----> CommandDeviceIdBase - {pducommand.PDUType} - {deviceid.Device.ToIdString()}");
//Debug.WriteLine($"----> CommandDeviceIdBase - {pducommand.PDUType} - {deviceid.Device.ToIdString()}");
IAperioBase? device = _devicesList.GetDevice(deviceid.Device);
if (device != null)
if (_devicesList.GetDevice(deviceid.Device) is IAperioBase device)
{
await device.SetConnectionState(ConnectionStates.Online, token);
await device.SetConnection(this, token);
await device.HandleMessage(pducommand, token);
}
//IAperioBase? device = _devicesList.GetDevice(deviceid.Device);
//if (device != null)
//{
// await device.SetConnectionState(ConnectionStates.Online, token);
// await device.SetConnection(this, token);
// await device.HandleMessage(pducommand, token);
//}
}
else if (pducommand is CommandIdBase id)
{
Debug.WriteLine($"----> CommandIdBase - {pducommand.PDUType} - {id.DeviceGroupId.Charaters}");
//Debug.WriteLine($"----> CommandIdBase - {pducommand.PDUType} - {id.DeviceGroupId.Charaters}");
IAperioBase? device = _devicesList.GetDevice(id.DeviceGroupId);
if (device != null)
if (_devicesList.GetDevice(id.DeviceGroupId) is IAperioBase device)
{
await device.SetConnectionState(ConnectionStates.Online, token);
await device.SetConnection(this, token);
await device.HandleMessage(pducommand, token);
}
//IAperioBase? device = _devicesList.GetDevice(id.DeviceGroupId);
//if (device != null)
//{
// await device.SetConnectionState(ConnectionStates.Online, token);
// await device.SetConnection(this, token);
// await device.HandleMessage(pducommand, token);
//}
}
else
{
@@ -318,7 +346,13 @@ namespace Aperio_Control_Centre.AperioServer
if (device != null)
{
device.ConnectionState = ConnectionStates.Offline;
await db.DisableDevice(device, null, token);
device.Active = ActiveStates.Inactive;
device.LastChanged = DateTime.UtcNow;
await db.Loggings.AddLogAsync(device, "Geblokkeerd", LogTypes.Device, token);
//await db.DisableDevice(device, null, token);
await db.SaveChangesAsync(token);
}
}
@@ -344,18 +378,22 @@ namespace Aperio_Control_Centre.AperioServer
}
private async Task DeviceParameterAction(DeviceId devId, DeviceParameterNotificationBlock block, CancellationToken token)
private void DeviceParameterAction(DeviceId devId, DeviceParameterNotificationBlock block)
{
Debug.WriteLine($"Device Parameter {block.Name.Name} - {block.Type} - {block.Value} {block.Result}");
//if (string.Compare(block.Name.Name, "FwVersion") == 0)
if (block.Name.Name.Equals("FwVersion", StringComparison.Ordinal))
{
IAperioBase? device = _devicesList.GetDevice(devId);
if (device != null)
if (_devicesList.GetDevice(devId) is IAperioBase device)
{
device.FirmwareVersion = block.GetValue<string>() ?? string.Empty;
}
//IAperioBase? device = _devicesList.GetDevice(devId);
//if (device != null)
//{
// device.FirmwareVersion = block.GetValue<string>() ?? string.Empty;
//}
}
else if (block.Name.Name.Equals("LockVersions", StringComparison.Ordinal))
@@ -367,11 +405,15 @@ namespace Aperio_Control_Centre.AperioServer
{
LockVersion lockVersion = new(arr);
//Debug.WriteLine($"LOCKVERSION this:{devId} - parameter:{lockVersion.Id.ToId()} - {lockVersion.Version}");
IAperioBase? device = _devicesList.GetDevice(lockVersion.Id);
if (device != null)
if (_devicesList.GetDevice(lockVersion.Id) is IAperioBase device)
{
device.LockVersion = lockVersion.Version;
}
//IAperioBase? device = _devicesList.GetDevice(lockVersion.Id);
//if (device != null)
//{
// device.LockVersion = lockVersion.Version;
//}
}
}
}

View File

@@ -105,7 +105,7 @@ namespace Aperio_Control_Centre.AperioServer
Logger.LogError(_logger, $"{aperioConnection.Address} - {aperioConnection.ConnectionId}", ex);
}
await aperioConnection.RemoveConnection();
aperioConnection.RemoveConnection();
_screenLogging.AddScreenlog(new SystemScreenLog()
{

View File

@@ -5,6 +5,7 @@ using Aperio_Control_Centre.ACSDatabase.Types;
using Aperio_Control_Centre.AperioServer.Devices;
using Aperio_Control_Centre.Components.Aperio;
using Aperio_Control_Centre.Log;
using Aperio_Control_Centre.Models;
using Aperio_Control_Centre.ScreenLogging;
using System.Collections.Concurrent;
using System.Diagnostics;
@@ -33,6 +34,19 @@ namespace Aperio_Control_Centre.AperioServer
return _deviceDictionary.Values.Where(c => c.Connection != null).Select(c => c.Connection).GroupBy(c => c.ConnectionId).Select(g => g.First());
}
public IEnumerable<DoorModeSchedule> GetSchedules()
{
//IEnumerable<SchedulerItem> ksldfkd = _deviceDictionary.Values.OfType<IAperioDoor>().SelectMany(s => s.DoorModeSchedules);
//foreach (var kvp in ksldfkd)
//{
// Debug.WriteLine($"Scheduler item = {kvp.DoorModeSchedule.Timezone.BeginTime} - {kvp.DoorModeSchedule.Timezone.EndTime} - {kvp.DoorModeSchedule.DoorMode} - {kvp.DoorModeSchedule.ExecuteMode} - {kvp.DoorModeSchedule.Device.Name}");
//}
return _deviceDictionary.Values.OfType<IAperioDoor>().SelectMany(s => s.DoorModeSchedules).Select(s => s.DoorModeSchedule);
}
public IEnumerable<IAperioBase> GetDevices(string connectionId)
{
return _deviceDictionary.Values.Where(c => c.Connection?.ConnectionId == connectionId);
@@ -182,6 +196,6 @@ namespace Aperio_Control_Centre.AperioServer
{
Message = $"Removed {devId} from devicelist"
});
}
}
}
}

View File

@@ -54,7 +54,7 @@ namespace Aperio_Control_Centre.AperioServer
public void ResetTimeout()
{
Debug.WriteLine("AperioTimeoutFeature Reset timeout");
//Debug.WriteLine("AperioTimeoutFeature Reset timeout");
_timeoutCounter = _timeoutValue;
}

View File

@@ -84,6 +84,23 @@ namespace Aperio_Control_Centre.AperioServer.Devices
}
return null;
}
//protected private async Task<int?> GetDatabaseDeviceId(ACSDatabaseContext db, CancellationToken token)
//{
// if (DataBaseDeviceID != 0)
// {
// //return await db.Devices.FindAsync([DataBaseDeviceID], token);
// return DataBaseDeviceID;
// }
// Device? device = await db.Devices.GetDeviceFromString(_deviceId.ToIdString(), token);
// if (device != null)
// {
// DataBaseDeviceID = device.Id;
// Name = device.Name;
// //return device;
// return DataBaseDeviceID;
// }
// return null;
//}
public virtual Task GetStatusFromDevice(CancellationToken token)
{
@@ -196,13 +213,14 @@ namespace Aperio_Control_Centre.AperioServer.Devices
DeviceStatus = deviceStatus;
//Debug.WriteLine($"DeviceStatus Changed to {_deviceStatus}");
using ACSDatabaseContext db = await _contextFactory.CreateDbContextAsync(token);
Device? dbDevice = await GetDatabaseDevice(db, token);
if (dbDevice != null)
{
await db.Loggings.AddLogAsync(dbDevice, DeviceStatus, token);
await db.SaveChangesAsync(token);
}
//TODO: Device status in db opslaan??, veel meldingen
//using ACSDatabaseContext db = await _contextFactory.CreateDbContextAsync(token);
//Device? dbDevice = await GetDatabaseDevice(db, token);
//if (dbDevice != null)
//{
// await db.Loggings.AddLogAsync(dbDevice, DeviceStatus, token);
// await db.SaveChangesAsync(token);
//}
_screenLogger.AddScreenlog(new SystemScreenLog()
{
IPAddress = Connection?.Address,

View File

@@ -2,7 +2,6 @@
using Aperio_Control_Centre.Aadp.Commands.DeviceStates;
using Aperio_Control_Centre.Aadp.Commands.DoorControl;
using Aperio_Control_Centre.Aadp.Commands.InputFromUser;
using Aperio_Control_Centre.Aadp.Commands.OperationAndMaintenance;
using Aperio_Control_Centre.Aadp.Commands.OutputToUser;
using Aperio_Control_Centre.Aadp.Enumerations;
using Aperio_Control_Centre.Aadp.Types.ApplicationTypes;
@@ -11,6 +10,8 @@ using Aperio_Control_Centre.ACSDatabase;
using Aperio_Control_Centre.ACSDatabase.Models;
using Aperio_Control_Centre.ACSDatabase.Models.Extensions;
using Aperio_Control_Centre.ACSDatabase.Types;
using Aperio_Control_Centre.Extensions;
using Aperio_Control_Centre.Models;
using Aperio_Control_Centre.ScreenLogging;
using Aperio_Control_Centre.StatusBroadcast;
using Microsoft.EntityFrameworkCore;
@@ -39,6 +40,7 @@ namespace Aperio_Control_Centre.AperioServer.Devices
dbDevice.DoorModes = _doorModes;
await db.SaveChangesAsync(token);
}
RaiseDeviceChanged();
}
public async Task GetSupportedDoorModesFromDevice(CancellationToken token)
@@ -65,6 +67,141 @@ namespace Aperio_Control_Centre.AperioServer.Devices
await SaveDoorModeToDatabase(token);
}
//------------- Door mode scheduler ---------------------------------
//public void SetDoorModeOnDevice(DoorMode? doorMode)
//{
// Debug.WriteLine($"Set door mode on device {Name} - {doorMode}");
// //if (checker.Device.DoorModes.Where(m => m.DoorMode is DoorMode.FREE_OPEN).Any())
// //{
// // await WriteToDevice(new SetDoorModeCommand(new Id(_deviceId.ToIdString()), DoorMode.FREE_OPEN, checker.Device!.OpenTimeFromNowDeciseconds(), TimingMode.TIME, ExecuteMode.ASAP), token);
// //}
// //else if (checker.Device.DoorModes.Where(m => m.DoorMode is DoorMode.FREE_UNLOCKED).Any())
// //{
// // await WriteToDevice(new SetDoorModeCommand(new Id(_deviceId.ToIdString()), DoorMode.FREE_UNLOCKED, checker.Device!.OpenTimeFromNowDeciseconds(), TimingMode.TIME, ExecuteMode.ASAP), token);
// //}
// //else
// //{
// // Debug.WriteLine("Not supportted door mode");
// //}
//}
public List<SchedulerItem> DoorModeSchedules { get; private set; } = [];
//private readonly List<SchedulerItem> _schedulerItems = [];
//public List<SchedulerItem> GetDoorModeSchedule()
//{
// return _schedulerItems;
//}
public async Task LoadDoorModeSchedule(CancellationToken token)
{
Debug.WriteLine("Load DoorModeSchedule In AperioDoor");
if (DoorModeSchedules.Count != 0)
{
foreach (var item in DoorModeSchedules)
{
if (item.BeginTimer != null)
{
item.BeginTimer.Change(Timeout.Infinite, Timeout.Infinite);
await item.BeginTimer.DisposeAsync();
}
if (item.EndTimer != null)
{
item.EndTimer.Change(Timeout.Infinite, Timeout.Infinite);
await item.EndTimer.DisposeAsync();
}
}
DoorModeSchedules.Clear();
}
//await WriteToDevice(new SetDoorModeCommand(_deviceId.ToId(), DoorMode.INSIDEOUT_LOCKED, ExecuteMode.ASAP), token);
using ACSDatabaseContext db = await _contextFactory.CreateDbContextAsync(token);
List<DoorModeSchedule> schedules = await db.DoorModeSchedules
.Where(d => d.DeviceId == DataBaseDeviceID)
.Include(t => t.Timezone)
.Include(d => d.Device)
.ToListAsync(token);
foreach (DoorModeSchedule item in schedules)
{
Debug.WriteLine($"ID:{item.Id} - Timezone:{item.Timezone?.GetTimezoneString} - device:{item.DeviceId} - {item.DoorMode} - {item.Timezone?.TwentyFourHours}");
if (item.Timezone?.WeekDays != WeekDaysTypes.None)
{
SchedulerItem schedulerItem = new()
{
DoorModeSchedule = item
};
Debug.WriteLine($"Seconds to next begin + 24hr {item.Timezone?.TimeToNextBeginTime().TotalSeconds}");
schedulerItem.BeginTimer = new Timer(BeginTimerCallback, schedulerItem, item.Timezone!.TimeToNextBeginTime(), Timeout.InfiniteTimeSpan);
//schedulerItem.BeginTimer = new Timer(BeginTimerCallback, schedulerItem, TimeSpan.FromSeconds(30), Timeout.InfiniteTimeSpan);
if (item.Timezone.IsTimezoneActive())
{
Debug.WriteLine("Time zone active");
await WriteToDevice(new SetDoorModeCommand(_deviceId.ToId(), item.DoorMode, item.Timezone.TimeToNextEndTime().ToDeciSeconds(), TimingMode.TIME, item.ExecuteMode), token);
}
if (item.Device?.ProductClass == ProductClass.OPTA_V1)
{
//Debug.WriteLine($"Seconds to next end + 24hr {item.Timezone.TimeToNextEndTime().TotalSeconds}");
//schedulerItem.EndTimer = new Timer(EndTimerCallback, schedulerItem, item.Timezone.TimeToNextEndTime(), Timeout.InfiniteTimeSpan);
//schedulerItem.EndTimer = new Timer(EndTimerCallback, schedulerItem, TimeSpan.FromSeconds(60), Timeout.InfiniteTimeSpan);
}
DoorModeSchedules.Add(schedulerItem);
}
}
}
private void BeginTimerCallback(object? state)
{
Debug.WriteLine("BeginTimerCallback");
//Logger.LogInformation(_logger, $"Timed Hosted Service is working.");
if (state is SchedulerItem item)
{
Debug.WriteLine($"{DateTime.Now} -----> BeginTimerCallback {item.DoorModeSchedule.Timezone?.GetTimezoneString} - {item.DoorModeSchedule.Device?.Name} - {item.DoorModeSchedule.DoorMode} - {item.DoorModeSchedule.ExecuteMode} - {item.DoorModeSchedule.Timezone.TimeToNextEndTime().ToDeciSeconds()}");
//SetDoorModeOnDevice(item.DoorModeSchedule.DoorMode);
//WriteToDevice(new SetDoorModeCommand(new Id(_deviceId.ToIdString()), DoorMode.FREE_OPEN, checker.Device!.OpenTimeFromNowDeciseconds(), TimingMode.TIME, ExecuteMode.ASAP), default).Wait();
//WriteToDevice(new SetDoorModeCommand(_deviceId, item.DoorModeSchedule.DoorMode.Value, 500, TimingMode.TIME, ExecuteMode.ASAP), default).Wait();
WriteToDevice(new SetDoorModeCommand(_deviceId.ToId(), item.DoorModeSchedule.DoorMode, item.DoorModeSchedule.Timezone.TimeToNextEndTime().ToDeciSeconds(), TimingMode.TIME, item.DoorModeSchedule.ExecuteMode), default).Wait();
item.BeginTimer = new Timer(BeginTimerCallback, item, item.DoorModeSchedule.Timezone.TimeToNextBeginTime(), Timeout.InfiniteTimeSpan);
//item.BeginTimer = new Timer(BeginTimerCallback, item, TimeSpan.FromMinutes(1), Timeout.InfiniteTimeSpan);
}
}
//private void EndTimerCallback(object? state)
//{
// Debug.WriteLine("EndTimerCallback");
// //Logger.LogInformation(_logger, $"Timed Hosted Service is working.");
// if (state is SchedulerItem item)
// {
// Debug.WriteLine($"{DateTime.Now} <------ EndTimerCallback {item.DoorModeSchedule.Timezone?.GetTimezoneString} - {item.DoorModeSchedule.Device?.Name} - {item.DoorModeSchedule.DoorMode}");
// //Debug.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId}");
// //SetDoorModeOnDevice(item.DoorModeSchedule.DoorMode);
// //SetDoorModeOnDevice(DoorMode.INSIDEOUT_LOCKED);
// //WriteToDevice(new SetDoorModeCommand(_deviceId, DoorMode.INSIDEOUT_LOCKED, 5, TimingMode.INFINITE, ExecuteMode.ASAP), default).Wait();
// WriteToDevice(new SetDoorModeCommand(_deviceId.ToId(), DoorMode.INSIDEOUT_LOCKED, ExecuteMode.ASAP), default).Wait();
// item.EndTimer = new Timer(EndTimerCallback, item, item.DoorModeSchedule.Timezone.TimeToNextEndTime(), Timeout.InfiniteTimeSpan);
// //item.EndTimer = new Timer(EndTimerCallback, item, TimeSpan.FromMinutes(1), Timeout.InfiniteTimeSpan);
// }
//}
//----------------- Door state -----------------------------------------------------
public async Task GetDoorStateFromDevice(CancellationToken token)
{
@@ -101,7 +238,7 @@ namespace Aperio_Control_Centre.AperioServer.Devices
if (LockState != lockState)
{
LockState = lockState;
//Debug.WriteLine($"Lock state Changed to {_lockState}");
Debug.WriteLine($"Lock state Changed to {lockState}");
//await _statusBroadcast.SendLockStatus(Id, _lockState, token);
using ACSDatabaseContext db = await _contextFactory.CreateDbContextAsync(token);
Device? dbDevice = await GetDatabaseDevice(db, token);
@@ -234,6 +371,12 @@ namespace Aperio_Control_Centre.AperioServer.Devices
return;
}
if (checker.Device?.DoorModeSchedules != null)
{
}
string logstring;
if (checker.Device?.UnlockTimezone != null)
{
@@ -269,9 +412,19 @@ namespace Aperio_Control_Centre.AperioServer.Devices
}
else
{
await UnlockDoor(checker.Device!.OpenTimeDeciseconds(), token);
logstring = $"Toegang {checker.Device.OpenTimeSeconds()} seconden";
DoorModeSchedule? doormode = checker.Device?.DoorModeSchedules.GetFirstActiveDoorModeschedule();
if(doormode != null)
{
await WriteToDevice(new SetDoorModeCommand(_deviceId.ToId(), doormode.DoorMode, doormode.Timezone.TimeToNextEndTime().ToDeciSeconds(), TimingMode.TIME, doormode.ExecuteMode), token);
logstring = $"Toegang tot {doormode.Timezone.EndTime}";
}
else
{
await UnlockDoor(checker.Device!.OpenTimeDeciseconds(), token);
logstring = $"Toegang {checker.Device.OpenTimeSeconds()} seconden";
}
}
await checker.UserLogMessage(logstring, AccessTypes.Granted, token);
await _statusBroadcast.SendWatched(checker.User, checker.Device, true, db, token);
}

View File

@@ -32,6 +32,7 @@ namespace Aperio_Control_Centre.AperioServer.Devices
await base.SetBaseDevice(deviceId, conn, token);
HubDeviceId = hostId;
await CreateDatabaseDevice(conn.Address.ToIpString(), token);
await LoadDoorModeSchedule(token);
}
private async Task CreateDatabaseDevice(string ipaddress, CancellationToken token)
@@ -192,14 +193,14 @@ namespace Aperio_Control_Centre.AperioServer.Devices
HandleState = handleState;
//Debug.WriteLine($"Handle state Changed to {_handleState}");
using ACSDatabaseContext db = await _contextFactory.CreateDbContextAsync(token);
Device? dbDevice = await GetDatabaseDevice(db, token);
if (dbDevice != null)
{
dbDevice.HandleState = HandleState;
await db.Loggings.AddLogAsync(dbDevice, HandleState, token);
await db.SaveChangesAsync(token);
}
//using ACSDatabaseContext db = await _contextFactory.CreateDbContextAsync(token);
//Device? dbDevice = await GetDatabaseDevice(db, token);
//if (dbDevice != null)
//{
// dbDevice.HandleState = HandleState;
// await db.Loggings.AddLogAsync(dbDevice, HandleState, token);
// await db.SaveChangesAsync(token);
//}
_screenLogger.AddScreenlog(new SystemScreenLog()
{
IPAddress = Connection?.Address,

View File

@@ -29,6 +29,7 @@ namespace Aperio_Control_Centre.AperioServer.Devices
ArgumentNullException.ThrowIfNull(conn);
await base.SetBaseDevice(deviceId, conn, token);
await CreateDatabaseDevice(conn.Address.ToIpString(), token);
await LoadDoorModeSchedule(token);
}
private async Task CreateDatabaseDevice(string ipaddress, CancellationToken token)

View File

@@ -1,6 +1,8 @@
using Aperio_Control_Centre.Aadp.Commands.InputFromUser;
using Aperio_Control_Centre.Aadp.Enumerations;
using Aperio_Control_Centre.Aadp.Types.ApplicationTypes;
using Aperio_Control_Centre.ACSDatabase.Models;
using Aperio_Control_Centre.Models;
namespace Aperio_Control_Centre.AperioServer.Devices
{
@@ -10,6 +12,10 @@ namespace Aperio_Control_Centre.AperioServer.Devices
public Task GetDoorModeFromDevice(CancellationToken token);
public DoorMode GetDoorMode();
//public void SetDoorModeOnDevice(DoorMode? doorMode);
public Task LoadDoorModeSchedule(CancellationToken token);
public List<SchedulerItem> DoorModeSchedules { get; }
public Task GetDoorStateFromDevice(CancellationToken token);
public DoorState DoorState { get; }

View File

@@ -14,7 +14,7 @@ namespace Aperio_Control_Centre.AperioServer
public IPAddress Address { get; }
public void SetConnection(ConnectionContext connection);
public Task RemoveConnection();
public void RemoveConnection();
public void Abort(ConnectionAbortedException abortReason);

View File

@@ -1,5 +1,7 @@
using Aperio_Control_Centre.Aadp.Types.ApplicationTypes;
using Aperio_Control_Centre.ACSDatabase.Models;
using Aperio_Control_Centre.AperioServer.Devices;
using Aperio_Control_Centre.Models;
namespace Aperio_Control_Centre.AperioServer
{
@@ -10,6 +12,8 @@ namespace Aperio_Control_Centre.AperioServer
public IEnumerable<IAperioBase> GetDevices();
public IEnumerable<IAperioConnectionContext?> GetConnections();
public IEnumerable<DoorModeSchedule> GetSchedules();
public IEnumerable<IAperioBase> GetDevices(string connectionId);
public IAperioBase? GetDevice(string devId);

View File

@@ -49,6 +49,10 @@
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.9" />
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="5.9.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Plotly.Blazor" Version="6.0.2" />
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="9.0.9" />
<PackageReference Include="System.Management" Version="9.0.9" />

View File

@@ -175,19 +175,35 @@
string message = string.Empty;
protected override async Task OnAfterRenderAsync(bool firstRender)
private PeriodicTimer refreshTimer = new(TimeSpan.FromSeconds(1));
// protected override async Task OnAfterRenderAsync(bool firstRender)
// {
// if (firstRender)
// {
// deviceList.DeviceListChanged += async (s, e) => await InvokeAsync(() => UpdateTable());
// await UpdateTable();
// }
// await base.OnAfterRenderAsync(firstRender);
// }
protected override async Task OnInitializedAsync()
{
if (firstRender)
await base.OnInitializedAsync();
deviceList.DeviceListChanged += async (s, e) => await InvokeAsync(() => UpdateTable());
devices = deviceList.GetDevices().AsQueryable();
while (await refreshTimer.WaitForNextTickAsync())
{
deviceList.DeviceListChanged += async (s, e) => await InvokeAsync(() => UpdateTable());
await UpdateTable();
}
await base.OnAfterRenderAsync(firstRender);
}
public void Dispose()
{
refreshTimer.Dispose();
deviceList.DeviceListChanged -= async (s, e) => await InvokeAsync(() => UpdateTable());
}

View File

@@ -0,0 +1,73 @@
@implements IDisposable
@inject IAperioDeviceList deviceList
@inject IStringLocalizer<Aperio_Control_Centre.Resources.ValidationResources> localizer
@using Aperio_Control_Centre.Models
@using Microsoft.AspNetCore.Components.QuickGrid
@using Microsoft.AspNetCore.Connections
@using Aperio_Control_Centre.Extensions
@attribute [Authorize]
<QuickGrid Items="@schedules" class="table table-hover" @ref="schedulesGrid">
<TemplateColumn Title="Name">
<a href="/Device/Edit/@context.Device?.Id">@context.Device?.Name</a>
</TemplateColumn>
<TemplateColumn Title="Timezone">
<a href="/Timezone/Edit/@context.Timezone?.Id">@context.Timezone?.GetTimezoneString</a>
</TemplateColumn>
<TemplateColumn Title="Begin Time">
@context.Timezone?.BeginTimeString()
<br />
@context.Timezone?.TimeToNextBeginTime().ToCustomString()
</TemplateColumn>
<TemplateColumn Title="End Time">
@context.Timezone?.EndTimeString()
<br />
@context.Timezone?.TimeToNextEndTime().ToCustomString()
</TemplateColumn>
<PropertyColumn Title="Door Mode" Property="@(p => p.DoorMode)" />
<PropertyColumn Title="Execute Mode" Property="@(p => p.ExecuteMode)" />
</QuickGrid>
@code {
private QuickGrid<DoorModeSchedule>? schedulesGrid;
private IQueryable<DoorModeSchedule>? schedules;
private PeriodicTimer refreshTimer = new(TimeSpan.FromSeconds(1));
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
schedules = deviceList.GetSchedules().AsQueryable();
while (await refreshTimer.WaitForNextTickAsync())
{
await UpdateTable();
}
}
public void Dispose()
{
refreshTimer?.Dispose();
}
private async Task UpdateTable()
{
schedules = deviceList.GetSchedules().AsQueryable();
await schedulesGrid!.RefreshDataAsync();
this.StateHasChanged();
}
// private void CloseConnection(IAperioConnectionContext conn)
// {
// conn.Abort(new("User closed connection"));
// }
}

View File

@@ -27,6 +27,14 @@ namespace Aperio_Control_Centre.Controllers
return View();
}
[HttpGet]
[Authorize]
public ActionResult Schedules()
{
ViewBag.Title = _localizer["Schedules"];
return View();
}
[HttpGet]
[Authorize]

View File

@@ -1,24 +1,92 @@
using Aperio_Control_Centre.Aadp.Enumerations;
using Aperio_Control_Centre.Aadp.Types.ApplicationTypes;
using Aperio_Control_Centre.ACSDatabase;
using Aperio_Control_Centre.ACSDatabase.Models;
using Aperio_Control_Centre.ACSDatabase.Models.Extensions;
using Aperio_Control_Centre.ACSDatabase.Types;
using Aperio_Control_Centre.AperioServer;
using Aperio_Control_Centre.AperioServer.Devices;
using Aperio_Control_Centre.Extensions;
using Aperio_Control_Centre.Models;
using Aperio_Control_Centre.PagedList;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization;
using OfficeOpenXml.Drawing.Chart;
using System;
using System.Data;
using System.Diagnostics;
using System.Security.Principal;
using Telegram.Bot.Types;
namespace Aperio_Control_Centre.Controllers
{
public class DeviceController(IDbContextFactory<ACSDatabaseContext> dbContextFactory, IStringLocalizer<DeviceController> localizer) : Controller
public class DeviceController(IAperioDeviceList deviceList, IDbContextFactory<ACSDatabaseContext> dbContextFactory, IStringLocalizer<DeviceController> localizer) : Controller
{
private readonly IAperioDeviceList _devicesList = deviceList;
private readonly IDbContextFactory<ACSDatabaseContext> _contextFactory = dbContextFactory;
private readonly IStringLocalizer<DeviceController> _localizer = localizer;
//private static async Task<Device> LoadDeviceParameters(Device device, ACSDatabaseContext db, CancellationToken token)
//{
// device.LocationSelection = await db.Locations.LocationSelectList(token);
// device.TimezoneSelection = await db.Timezones.TimeZoneSelectList(token);
// device.DeviceGroupsSelectList = await db.DeviceGroups.DeviceGroupsListItems(device, token);
// device.LastLoggings = await db.Loggings.LastDeviceLoggings(device, 25, token);
// device.DoorModes = (await db.Devices.FindAsync([device.Id], token))?.DoorModes;
// //if (device.DoorModeSchedules != null)
// //{
// // //await device.DoorModeScheduler.LoadTimezones(db, token);
// //}
// //device.ExecuteModeSelection = ExecuteModeSelectList();
// return device;
//}
//private static List<SelectListItem> ExecuteModeSelectItems()
//{
// return Enum.GetValues<ExecuteMode>().Cast<ExecuteMode>().Select(static s => new SelectListItem
// {
// Value = s.GetDisplayName(),
// Text = s.GetDisplayName()
// }).ToList();
// //return locations
// // .Where(a => a.Active != ActiveStates.Deleted)
// // .Select(s => new SelectListItem
// // {
// // Value = s.Id.ToString(),
// // Text = s.AddressString,
// // }).ToListAsync(token);
//}
//private static SelectList ExecuteModeSelectList()
//{
// //return new SelectList(ExecuteMode.Select(u => new { Value = u.ID, Text = u.Name }), "Value", "Text");
// var enumlist = Enum.GetValues<ExecuteMode>().Cast<ExecuteMode>().Select(s => new SelectListItem
// {
// Value = s.GetDisplayName(),
// Text = s.GetDisplayName()
// }).ToList();
// return new SelectList(enumlist, "Value", "Text");
//}
// GET: /Device/
[HttpGet]
[Authorize(Roles = "Administrator, DeviceAdmin, Viewer")]
@@ -123,6 +191,8 @@ namespace Aperio_Control_Centre.Controllers
device.LocationSelection = await db.Locations.LocationSelectList(token);
device.TimezoneSelection = await db.Timezones.TimeZoneSelectList(token);
device.DeviceGroupsSelectList = await db.DeviceGroups.DeviceGroupsListItems(token);
ModelState.AddModelError(string.Empty, "Model invalid");
return View(device);
@@ -143,6 +213,7 @@ namespace Aperio_Control_Centre.Controllers
Device? device = await db.Devices
.Where(d => d.Id == id)
.Include(l => l.Location)
.Include(d => d.DoorModeSchedules)
.SingleOrDefaultAsync(token);
if (device == null)
@@ -162,6 +233,7 @@ namespace Aperio_Control_Centre.Controllers
device.TimezoneSelection = await db.Timezones.TimeZoneSelectList(token);
device.DeviceGroupsSelectList = await db.DeviceGroups.DeviceGroupsListItems(device, token);
device.LastLoggings = await db.Loggings.LastDeviceLoggings(device, 25, token);
//device.DoorModes = (await db.Devices.FindAsync([device.Id], token))?.DoorModes;
ViewBag.Title = _localizer["Edit"];
@@ -182,6 +254,7 @@ namespace Aperio_Control_Centre.Controllers
Device? device = await db.Devices
.Where(x => x.Id == deviceEdit.Id)
.Include(g => g.DeviceGroups)
.Include(s => s.DoorModeSchedules)
.SingleOrDefaultAsync(token);
if (device == null)
@@ -191,6 +264,10 @@ namespace Aperio_Control_Centre.Controllers
//https://learn.microsoft.com/nl-nl/ef/core/saving/disconnected-entities
db.Entry(device).CurrentValues.SetValues(deviceEdit);
//Debug.WriteLine(db.ChangeTracker.DebugView.LongView);
//db.ChangeTracker.DetectChanges();
//Debug.WriteLine(db.ChangeTracker.DebugView.LongView);
if (device.ProductClass != ProductClass.GATEWAY)
{
if (deviceEdit.DeviceGroupsSelectList != null)
@@ -219,6 +296,23 @@ namespace Aperio_Control_Centre.Controllers
}
}
}
if (deviceEdit.DoorModeSchedules != null)
{
device.DoorModeSchedules = [];
//Debug.WriteLine($"DoorModeScheduler count = {deviceEdit.DoorModeSchedules?.Count}");
foreach (var item in deviceEdit.DoorModeSchedules)
{
//Debug.WriteLine($"{item.DoorMode} - {item.TimezoneId} - {item.DeviceId}");
item.DeviceId = device.Id;
device.DoorModeSchedules?.Add(item);
}
}
else
{
device.DoorModeSchedules = [];
}
}
if (device.ProductClass == ProductClass.GATEWAY)
@@ -242,11 +336,34 @@ namespace Aperio_Control_Centre.Controllers
await db.AddLogAsync(User.Identity, device, "Apparaat bewerkt", LogTypes.Device, token);
await db.SaveChangesAsync(token);
if (device.ProductClass != ProductClass.GATEWAY)
{
if (_devicesList.GetDevice(device.DeviceId) is IAperioDoor door)
{
await door.LoadDoorModeSchedule(token);
}
}
return Request.Cookies.RedirectToCookie();
}
//deviceEdit.LocationSelection = await db.Locations.LocationSelectList(token);
//deviceEdit.TimezoneSelection = await db.Timezones.TimeZoneSelectList(token);
//deviceEdit.DeviceGroupsSelectList = await db.DeviceGroups.DeviceGroupsListItems(deviceEdit, token);
//deviceEdit.LastLoggings = await db.Loggings.LastDeviceLoggings(deviceEdit, 25, token);
//deviceEdit.DoorModes = (await db.Devices.FindAsync([deviceEdit.Id], token))?.DoorModes;
//if (deviceEdit.DoorModeScheduler != null)
//{
// await deviceEdit.DoorModeScheduler.LoadTimezones(db, token);
//}
//deviceEdit = await LoadDeviceParameters(deviceEdit, db, token);
deviceEdit.LocationSelection = await db.Locations.LocationSelectList(token);
deviceEdit.TimezoneSelection = await db.Timezones.TimeZoneSelectList(token);
deviceEdit.DeviceGroupsSelectList = await db.DeviceGroups.DeviceGroupsListItems(deviceEdit, token);
deviceEdit.LastLoggings = await db.Loggings.LastDeviceLoggings(deviceEdit, 25, token);
deviceEdit.DoorModes = (await db.Devices.FindAsync([deviceEdit.Id], token))?.DoorModes;
ModelState.AddModelError(string.Empty, "Model invalid");
return View(deviceEdit);
@@ -268,6 +385,7 @@ namespace Aperio_Control_Centre.Controllers
.Where(d => d.Id == id)
.Include(l => l.Location)
.Include(t => t.UnlockTimezone)
.Include(s => s.DoorModeSchedules!).ThenInclude(t => t.Timezone)
.SingleOrDefaultAsync(token);
if (device == null)
@@ -319,6 +437,7 @@ namespace Aperio_Control_Centre.Controllers
.Where(d => d.Id == id)
.Include(l => l.Location)
.Include(t => t.UnlockTimezone)
.Include(s => s.DoorModeSchedules!).ThenInclude(t => t.Timezone)
.SingleOrDefaultAsync(token);
if (device == null)
@@ -345,7 +464,10 @@ namespace Aperio_Control_Centre.Controllers
{
return NotFound();
}
await db.DisableDevice(device, User.Identity, token);
device.Active = ActiveStates.Inactive;
device.LastChanged = DateTime.UtcNow;
await db.AddLogAsync(User.Identity, device, "Geblokkeerd", LogTypes.Device, token);
await db.SaveChangesAsync(token);
return Request.Cookies.RedirectToCookie();
}
@@ -366,6 +488,7 @@ namespace Aperio_Control_Centre.Controllers
.Where(d => d.Id == id)
.Include(l => l.Location)
.Include(t => t.UnlockTimezone)
.Include(s => s.DoorModeSchedules!).ThenInclude(t => t.Timezone)
.SingleOrDefaultAsync(token);
if (device == null)
@@ -393,7 +516,6 @@ namespace Aperio_Control_Centre.Controllers
}
device.Active = ActiveStates.Active;
device.LastChanged = DateTime.UtcNow;
db.Devices.Update(device);
await db.AddLogAsync(User.Identity, device, "Geactiveerd", LogTypes.Device, token);
await db.SaveChangesAsync(token);

View File

@@ -0,0 +1,14 @@
using Aperio_Control_Centre.Aadp.Enumerations;
using Aperio_Control_Centre.ACSDatabase.Models;
namespace Aperio_Control_Centre.Extensions
{
public static class DoorModeScheduleExtensions
{
public static string GetFullDescription(this DoorModeSchedule doorModeSchedule)
{
//return $"{doorModeSchedule.Timezone?.Name} - {doorModeSchedule.Timezone?.GetTimezoneString} - {doorModeSchedule.DoorMode} - {doorModeSchedule.ExecuteMode.GetDisplayName()}";
return $"{doorModeSchedule.Timezone?.Name} - {doorModeSchedule.Timezone?.GetTimezoneString} - {doorModeSchedule.DoorMode} - {doorModeSchedule.ExecuteMode}";
}
}
}

View File

@@ -334,9 +334,9 @@ namespace Aperio_Control_Centre.Extensions
switch (state)
{
case HandleState.NOT_USED:
return CreateBlazorStateIcon("orange", size, "Kruk niet gebruikt", "eye-off");
return CreateBlazorStateIcon("green", size, "Kruk niet gebruikt", "arrow-up");
case HandleState.BOTH_USED:
return CreateBlazorStateIcon("green", size, "Kruk gebruikt", "eye");
return CreateBlazorStateIcon("orange", size, "Kruk gebruikt", "arrow-down");
default:
break;
}

View File

@@ -0,0 +1,19 @@
namespace Aperio_Control_Centre.Extensions
{
public static class TimeSpanExtensions
{
public static uint ToDeciSeconds(this TimeSpan value)
{
return (uint)value.TotalSeconds * 10;
}
public static string ToCustomString(this TimeSpan value)
{
if (value.Days == 0)
{
return $"{value.Hours}:hr {value.Minutes}:min {value.Seconds}:sec";
}
return $"{value.Days}:days {value.Hours}:hr {value.Minutes}:min {value.Seconds}:sec";
}
}
}

View File

@@ -0,0 +1,12 @@
using Aperio_Control_Centre.ACSDatabase.Models;
namespace Aperio_Control_Centre.Models
{
public class SchedulerItem
{
public DoorModeSchedule DoorModeSchedule { get; set; }
public Timer? BeginTimer { get; set; }
public Timer? EndTimer { get; set; }
}
}

View File

@@ -66,6 +66,7 @@ namespace Aperio_Control_Centre
builder.Logging.AddConsole();
//builder.Logging.AddDebug();
//TODO: remove event logging
const string eventLogSource = "Aperio Control Centre";
if (!EventLog.SourceExists(eventLogSource))
{
@@ -103,6 +104,7 @@ namespace Aperio_Control_Centre
eventLogSettings.LogName = eventLogSource;
eventLogSettings.SourceName = eventLogSource;
});
builder.Logging.AddFileLogger();
//builder.Logging.AddFileLogger(configuration =>
//{
@@ -167,7 +169,8 @@ namespace Aperio_Control_Centre
options.MigrationsAssembly("Aperio_Control_Centre.ACSDatabase");
});
#if DEBUG
//.EnableSensitiveDataLogging()
options.EnableSensitiveDataLogging();
options.EnableDetailedErrors();
#endif
options.UseSeeding((context, _) =>
{
@@ -266,6 +269,15 @@ namespace Aperio_Control_Centre
builder.Services.AddTelegramBot(builder.Configuration.GetSection("Telegram"), _programCancellationTokenSource.Token);
//ACS Scheduler
//builder.Services.AddHostedService<Scheduler.SchedulerService>();
//builder.Services.Configure<AperioServerOptions>(ctx =>
//{
// ctx.TimeOutValue = builder.Configuration.GetValue<int>("Aperio:ConnectionTimeout");

View File

@@ -0,0 +1,189 @@
//using Aperio_Control_Centre.ACSDatabase;
//using Aperio_Control_Centre.ACSDatabase.Models;
//using Aperio_Control_Centre.AperioServer;
//using Aperio_Control_Centre.AperioServer.Devices;
//using Aperio_Control_Centre.Log;
//using Microsoft.EntityFrameworkCore;
//using System.Diagnostics;
//namespace Aperio_Control_Centre.Scheduler
//{
// public class SchedulerItem
// {
// public DoorModeSchedule DoorModeSchedule { get; set; }
// public Timer? BeginTimer { get; set; }
// public Timer? EndTimer { get; set; }
// }
// //public sealed class SchedulerService : IHostedService, IDisposable
// //{
// // private readonly ILogger<SchedulerService> _logger;
// // private readonly IDbContextFactory<ACSDatabaseContext> _dbContextFactory;
// // private readonly IAperioDeviceList _devicesList;
// // private readonly List<SchedulerItem> _items = [];
// // public SchedulerService(IAperioDeviceList devicesList, ILogger<SchedulerService> logger, IDbContextFactory<ACSDatabaseContext> dbContextFactory)
// // {
// // _devicesList = devicesList;
// // _logger = logger;
// // _dbContextFactory = dbContextFactory;
// // }
// // public async Task StartAsync(CancellationToken stoppingToken)
// // {
// // //_logger.LogInformation("Timed Hosted Service running.");
// // Logger.LogInformation(_logger, "Timed Hosted Service running.");
// // await LoadSchedulerItems(stoppingToken);
// // }
// // public async Task LoadSchedulerItems(CancellationToken token)
// // {
// // using ACSDatabaseContext db = await _dbContextFactory.CreateDbContextAsync(token);
// // List<DoorModeSchedule> schedules = await db.DoorModeSchedules
// // .Include(t => t.Timezone)
// // .Include(d => d.Device)
// // .ToListAsync(token);
// // foreach (DoorModeSchedule item in schedules)
// // {
// // AddSchedulerItem(item);
// // //Debug.WriteLine($"ID:{item.Id} - Timezone:{item.Timezone?.GetTimezoneString} - device:{item.Device?.Name} - {item.DoorMode}");
// // //SchedulerItem schedulerItem = new();
// // //schedulerItem.DoorModeSchedule = item;
// // //Debug.WriteLine($"Seconds to next begin + 24hr {item.Timezone.TimeToNextBeginTime().TotalSeconds}");
// // ////schedulerItem.BeginTimer = new Timer(BeginTimerCallback, schedulerItem, item.Timezone.TimeToNextBeginTime(), Timeout.InfiniteTimeSpan);
// // //schedulerItem.BeginTimer = new Timer(BeginTimerCallback, schedulerItem, TimeSpan.FromMinutes(1), Timeout.InfiniteTimeSpan);
// // //Debug.WriteLine($"Seconds to next end + 24hr {item.Timezone.TimeToNextEndTime().TotalSeconds}");
// // ////schedulerItem.EndTimer = new Timer(EndTimerCallback, schedulerItem, item.Timezone.TimeToNextEndTime(), Timeout.InfiniteTimeSpan);
// // //schedulerItem.EndTimer = new Timer(EndTimerCallback, schedulerItem, TimeSpan.FromMinutes(1), Timeout.InfiniteTimeSpan);
// // //_items.Add(schedulerItem);
// // }
// // }
// // public void AddSchedulerItem(DoorModeSchedule item)
// // {
// // Debug.WriteLine($"ID:{item.Id} - Timezone:{item.Timezone?.GetTimezoneString} - device:{item.Device?.Name} - {item.DoorMode}");
// // SchedulerItem schedulerItem = new()
// // {
// // DoorModeSchedule = item
// // };
// // Debug.WriteLine($"Seconds to next begin + 24hr {item.Timezone.TimeToNextBeginTime().TotalSeconds}");
// // //schedulerItem.BeginTimer = new Timer(BeginTimerCallback, schedulerItem, item.Timezone.TimeToNextBeginTime(), Timeout.InfiniteTimeSpan);
// // schedulerItem.BeginTimer = new Timer(BeginTimerCallback, schedulerItem, TimeSpan.FromMinutes(1), Timeout.InfiniteTimeSpan);
// // Debug.WriteLine($"Seconds to next end + 24hr {item.Timezone.TimeToNextEndTime().TotalSeconds}");
// // //schedulerItem.EndTimer = new Timer(EndTimerCallback, schedulerItem, item.Timezone.TimeToNextEndTime(), Timeout.InfiniteTimeSpan);
// // schedulerItem.EndTimer = new Timer(EndTimerCallback, schedulerItem, TimeSpan.FromMinutes(1), Timeout.InfiniteTimeSpan);
// // _items.Add(schedulerItem);
// // }
// // private void BeginTimerCallback(object? state)
// // {
// // //Logger.LogInformation(_logger, $"Timed Hosted Service is working.");
// // //Debug.WriteLine($"DoWork Fired");
// // if (state is SchedulerItem item)
// // {
// // Debug.WriteLine($"-----> BeginTimerCallback {item.DoorModeSchedule.Timezone?.GetTimezoneString} - {item.DoorModeSchedule.Device?.Name}");
// // if(_devicesList.GetDevice(item.DoorModeSchedule.Device?.DeviceId) is IAperioDoor door)
// // {
// // door.SetDoorModeOnDevice(item.DoorModeSchedule.DoorMode);
// // }
// // //IAperioBase? device = _devicesList.GetDevice(item.DoorModeSchedule.Device?.DeviceId);
// // //if (device != null)
// // //{
// // // if (device is IAperioDoor door)
// // // {
// // // door.SetDoorModeOnDevice(item.DoorModeSchedule.DoorMode);
// // // }
// // //}
// // else
// // {
// // Debug.WriteLine($"SchedulerService - {item.DoorModeSchedule.Device.Name} device not connected");
// // }
// // //item.BeginTimer = new Timer(BeginTimerCallback, item, item.DoorModeSchedule.Timezone.TimeToNextBeginTime(), Timeout.InfiniteTimeSpan);
// // item.BeginTimer = new Timer(BeginTimerCallback, item, TimeSpan.FromMinutes(1), Timeout.InfiniteTimeSpan);
// // }
// // }
// // private void EndTimerCallback(object? state)
// // {
// // //Logger.LogInformation(_logger, $"Timed Hosted Service is working.");
// // //Debug.WriteLine($"DoWork Fired");
// // if (state is SchedulerItem item)
// // {
// // Debug.WriteLine($"<------ EndTimerCallback {item.DoorModeSchedule.Timezone?.GetTimezoneString} - {item.DoorModeSchedule.Device?.Name}");
// // if (_devicesList.GetDevice(item.DoorModeSchedule.Device?.DeviceId) is IAperioDoor door)
// // {
// // door.SetDoorModeOnDevice(item.DoorModeSchedule.DoorMode);
// // }
// // //IAperioBase? device = _devicesList.GetDevice(item.DoorModeSchedule.Device?.DeviceId);
// // //if (device != null)
// // //{
// // // if (device is IAperioDoor door)
// // // {
// // // door.SetDoorModeOnDevice(item.DoorModeSchedule.DoorMode);
// // // }
// // //}
// // else
// // {
// // Debug.WriteLine($"SchedulerService - {item.DoorModeSchedule.Device.Name} device not connected");
// // }
// // //item.EndTimer = new Timer(EndTimerCallback, item, item.DoorModeSchedule.Timezone.TimeToNextEndTime(), Timeout.InfiniteTimeSpan);
// // item.EndTimer = new Timer(EndTimerCallback, item, TimeSpan.FromMinutes(1), Timeout.InfiniteTimeSpan);
// // }
// // }
// // public Task StopAsync(CancellationToken stoppingToken)
// // {
// // ///_logger.LogInformation("Timed Hosted Service is stopping.");
// // Logger.LogInformation(_logger, "Timed Hosted Service is stopping.");
// // //_timer?.Change(Timeout.Infinite, 0);
// // foreach (SchedulerItem item in _items)
// // {
// // item.BeginTimer?.Change(Timeout.Infinite, 0);
// // item.EndTimer?.Change(Timeout.Infinite, 0);
// // }
// // return Task.CompletedTask;
// // }
// // public void Dispose()
// // {
// // //_timer?.Dispose();
// // foreach (SchedulerItem item in _items)
// // {
// // item.BeginTimer?.Dispose();
// // item.EndTimer?.Dispose();
// // }
// // }
// //}
//}

View File

@@ -0,0 +1,62 @@
function indexDoorModeScheduler() {
//var doorModeScheduler = document.getElementById("DoorModeScheduler");
var li = document.getElementById("DoorModeScheduler").getElementsByTagName("li");
for (var i = 0; i < li.length; i++) {
//console.log("li " + i);
(li[i].querySelector("#timezoneid") as HTMLInputElement).name = "DoorModeSchedules[" + i + "].TimezoneId";
(li[i].querySelector("#doormode") as HTMLInputElement).name = "DoorModeSchedules[" + i + "].DoorMode";
(li[i].querySelector("#executemode") as HTMLInputElement).name = "DoorModeSchedules[" + i + "].ExecuteMode";
}
}
function removeDoorModeSchedulerItem(button) {
//console.log("removeDoorModeSchedulerItem");
button.parentElement.remove();
indexDoorModeScheduler();
}
function addDoorModeSchedulerItem(button: any) {
//console.log("addDoorModeSchedulerItem");
var timezoneSelect = (document.getElementById("UnlockTimezoneIdAdd")) as HTMLSelectElement;
var doormodeSelect = (document.getElementById("DoorModeAdd")) as HTMLSelectElement;
var executemodeSelect = (document.getElementById("ExecuteModeAdd")) as HTMLSelectElement;
if (timezoneSelect.selectedIndex != 0 && doormodeSelect.selectedIndex != 0 && executemodeSelect.selectedIndex != 0) {
var li = document.createElement("li") as HTMLLIElement;
li.innerHTML = timezoneSelect.options[timezoneSelect.selectedIndex].text + " - " + doormodeSelect.value + " - " + executemodeSelect.value;
li.className = "list-group-item d-flex justify-content-between align-items-center";
var timezoneidinput = document.createElement("input") as HTMLInputElement;
timezoneidinput.id = "timezoneid";
timezoneidinput.name = "DoorModeSchedules[].TimezoneId";
timezoneidinput.value = timezoneSelect.value;
timezoneidinput.type = "hidden";
li.appendChild(timezoneidinput);
var doormodeinput = document.createElement("input") as HTMLInputElement;
doormodeinput.id = "doormode";
doormodeinput.name = "DoorModeSchedules[].DoorMode";
doormodeinput.value = doormodeSelect.value;
doormodeinput.type = "hidden";
li.appendChild(doormodeinput);
var executemodeinput = document.createElement("input") as HTMLInputElement;
executemodeinput.id = "executemode";
executemodeinput.name = "DoorModeSchedules[].ExecuteMode";
executemodeinput.value = executemodeSelect.value;
executemodeinput.type = "hidden";
li.appendChild(executemodeinput);
var removebutton = document.createElement("button") as HTMLButtonElement;
removebutton.className = "btn btn-outline-danger";
removebutton.innerHTML = "remove";
removebutton.onclick = function () { removeDoorModeSchedulerItem(this); };
li.appendChild(removebutton);
document.getElementById("DoorModeScheduler").appendChild(li);
indexDoorModeScheduler();
}
}

View File

@@ -0,0 +1,7 @@
@Html.PageTitle((string)ViewBag.Title, (string)ViewBag.Message)
<div class="row justify-content-center p-0 m-0">
<component type="typeof(Aperio_Control_Centre.Components.Aperio.AperioSchedules)" render-mode="Server" />
</div>
<script src="_framework/blazor.web.js"></script>

View File

@@ -3,6 +3,8 @@
@inject IConfiguration Configuration
@model Aperio_Control_Centre.ACSDatabase.Models.Device
<script src="~/js/DoorModeScheduler.js"></script>
@Html.PageTitle((string)ViewBag.Title, (string)ViewBag.Message)
@using (Html.BeginForm())
@@ -10,7 +12,7 @@
@Html.AntiForgeryToken()
<div class="row">
<div class="col-lg-10 offset-lg-1 col-xl-6 offset-xl-3">
<div class="col-lg-10 offset-lg-1 col-xl-8 offset-xl-2">
@if (!ViewData.ModelState.IsValid)
{
@@ -91,10 +93,59 @@
</div>
</div>
<div class="row mb-3">
@Html.LabelFor(model => model.DoorModeSchedules, new { @class = "col-form-label col-sm-2" })
<div class="col-sm-10">
<ul class="list-group" id="DoorModeScheduler" name="DoorModeScheduler">
@for (int i = 0; i < Model.DoorModeSchedules?.Count(); i++)
{
<li class="list-group-item d-flex justify-content-between align-items-center">
@Model.DoorModeSchedules[i].GetFullDescription()
<input id="timezoneid" name="DoorModeSchedules[@i].TimezoneId" type="hidden" value="@Model.DoorModeSchedules[i].TimezoneId" />
<input id="doormode" name="DoorModeSchedules[@i].DoorMode" type="hidden" value="@Model.DoorModeSchedules[i].DoorMode" />
<input id="executemode" name="DoorModeSchedules[@i].ExecuteMode" type="hidden" value="@Model.DoorModeSchedules[i].ExecuteMode" />
<button class="btn btn-outline-danger" type="button" onclick="removeDoorModeSchedulerItem(this)">remove</button>
</li>
}
</ul>
<br />
<div class="input-group">
@if (Model.DoorModes != null)
{
@Html.DropDownList("UnlockTimezoneIdAdd", Model.TimezoneSelection, "Selecteer tijdzone", htmlAttributes: new { @class = "form-select" })
@Html.DropDownList("DoorModeAdd", Model.DoorModes?.DoorModeSelectList(), "Selecteer deur stand", htmlAttributes: new { @class = "form-select" })
@Html.DropDownList("ExecuteModeAdd", new SelectList(Enum.GetValues(typeof(ExecuteMode))), "Selecteer uitvoering", htmlAttributes: new { @class = "form-select" })
<button class="btn btn-outline-info" id="add" type="button" onclick="addDoorModeSchedulerItem(this)">Add</button>
}
else
{
<select class="form-select" disabled><option value="">Selecteer tijdzone</option></select>
<select class="form-select" disabled><option value="">Selecteer deur stand</option></select>
<select class="form-select" disabled><option value="">Selecteer uitvoering</option></select>
<button class="btn btn-outline-secondary" type="button" disabled>Add</button>
}
</div>
</div>
</div>
<div class="row mb-3">
@Html.LabelFor(model => model.UnlockTime, htmlAttributes: new { @class = "col-form-label col-sm-2" })
<div class="col-sm-10">
@Html.EditorFor(model => model.UnlockTime, new { htmlAttributes = new { @class = "form-control" } })
<div class="input-group">
@Html.EditorFor(model => model.UnlockTime, new { htmlAttributes = new { @class = "form-control" } })
<span class="input-group-text">Seconden</span>
</div>
@Html.ValidationMessageFor(model => model.UnlockTime, "", new { @class = "text-danger" })
</div>
</div>
@@ -103,12 +154,17 @@
@Html.LabelFor(model => model.DoorState, htmlAttributes: new { @class = "col-form-label col-sm-2" })
<div class="col-sm-10">
@Html.LockStateIcon(Model.LockState, 15)
@Html.HiddenFor(model => model.LockState)
@if (Model.ProductClass == ProductClass.LOCK)
{
@Html.DoorStateIcon(Model.DoorState, 15)
@Html.HiddenFor(model => model.DoorState)
@Html.HandleStateIcon(Model.HandleState, 15)
@Html.HiddenFor(model => model.HandleState)
@Html.KeyCylinderStateIcon(Model.KeyCylinderState, 15)
@Html.HiddenFor(model => model.KeyCylinderState)
@Html.TamperStateIcon(Model.TamperState, 15)
@Html.HiddenFor(model => model.TamperState)
}
</div>
</div>
@@ -119,6 +175,7 @@
@Html.LabelFor(model => model.BatteryState, htmlAttributes: new { @class = "col-form-label col-sm-2" })
<div class="col-sm-10">
@Html.BatteryStateIcon(Model.BatteryState, 15)
@Html.HiddenFor(model => model.BatteryState)
</div>
</div>
}
@@ -139,6 +196,7 @@
</div>
}
}
@Html.HiddenFor(model => model.DoorModes)
</div>
</div>
@@ -158,6 +216,7 @@
@Html.LabelFor(model => model.Ipaddress, new { @class = "col-form-label col-sm-2" })
<div class="col-sm-1">
@Html.ConnectionStateIcon(Model.ConnectionState, 15)
@Html.HiddenFor(model => model.ConnectionState)
</div>
<div class="col-sm-9">
@if (Model.ProductClass == ProductClass.H1502R11)
@@ -168,6 +227,7 @@
{
@Html.DisplayFor(model => model.Ipaddress)
}
@Html.HiddenFor(model => model.Ipaddress)
</div>
</div>
@@ -182,8 +242,10 @@
@Html.CheckBoxFor(model => model.DeviceGroupsSelectList[i].Selected, new { @checked = "checked", @class = "form-check-input", @id = $"DeviceGroupsSelectList_{i}" })
@Html.HiddenFor(model => model.DeviceGroupsSelectList[i].Value, new { @id = $"DeviceGroupsSelectList_{i}" })
@Html.HiddenFor(model => model.DeviceGroupsSelectList[i].Text, new { @id = $"DeviceGroupsSelectList_{i}" })
@* @Html.TimeZoneActiveIcon(Model.DeviceGroupsSelectList[i].TimeZoneActive, 10) *@
<label class="form-check-label" for="@Html.Raw($"DeviceGroupsSelectList_{i}")">
@Html.DisplayName(Model.DeviceGroupsSelectList[i].Text)
@Html.TimeZoneActiveIcon(Model.DeviceGroupsSelectList[i].TimeZoneActive, 8)
</label>
</div>
}

View File

@@ -76,6 +76,23 @@
<dd class="col-sm-9">@Html.TamperStateIcon(Model.TamperState, 15)</dd>
}
<dt class="col-sm-3">@Html.DisplayNameFor(model => model.DoorModeSchedules)</dt>
if (Model.DoorModeSchedules?.Count > 0)
{
foreach (var item in Model.DoorModeSchedules)
{
<dd class="col-sm-9 offset-sm-3 my-0">
@* @Html.DisplayName(item.FullName()) *@
@Html.DisplayName(item.GetFullDescription())
</dd>
}
}
else
{
<dd class="col-sm-9"></dd>
}
<dt class="col-sm-3">@Html.DisplayNameFor(model => model.UnlockTimezone)</dt>
<dd class="col-sm-9">@Html.DisplayFor(model => model.UnlockTimezone!.Name)</dd>

View File

@@ -142,6 +142,7 @@
<ul class="dropdown-menu">
<li>@Html.ActionLink(Localizer["Connections"].Value, "Connections", "Aperio", null, new { @class = "dropdown-item" })</li>
<li>@Html.ActionLink(Localizer["Devices"].Value, "Devices", "Aperio", null, new { @class = "dropdown-item" })</li>
<li>@Html.ActionLink(Localizer["Schedules"].Value, "Schedules", "Aperio", null, new { @class = "dropdown-item" })</li>
<li>@Html.ActionLink(Localizer["Performance"].Value, "AperioMetrics", "Metrics", null, new { @class = "dropdown-item" })</li>
<li><hr class="dropdown-divider"></li>
<li>@Html.ActionLink(Localizer["Aperio logging"].Value, "AperioLive", "Loggings", null, new { @class = "dropdown-item" })</li>

View File

@@ -4,7 +4,7 @@
"libraries": [
{
"provider": "cdnjs",
"library": "bootstrap@5.3.7",
"library": "bootstrap@5.3.8",
"destination": "wwwroot/bootstrap/",
"files": [
"css/bootstrap-grid.min.css",

View File

@@ -0,0 +1,18 @@
{
"compileOnSave": true,
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": true,
"sourceMap": false,
"target": "es6",
"outDir": "wwwroot/js"
},
"exclude": [
"node_modules",
"wwwroot"
],
"include": [
"scripts/**/*"
]
}

View File

@@ -82,7 +82,6 @@
$("#PassNumberBlock").hide();
}
}
});
$.extend($.expr[":"], {