Load DeviceSettings.config as an overlay on HikvisionServiceConfig

After loading serviceconfig.json, apply DeviceSettings.config values onto the existing config properties without changing runtime behavior or property names.
main
SYED MUSTUFA AHMED NAQVI 2026-08-05 15:36:09 +05:00
parent 3bc691933a
commit 920b8d1e0c
1 changed files with 90 additions and 2 deletions

View File

@ -237,7 +237,7 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
{
if (!File.Exists(configPath))
{
return new HikvisionServiceConfig
var missingCfg = new HikvisionServiceConfig
{
LogDirectory = @"C:\Users\Public\HikvisionAttendanceService",
SqlConnectionString = "",
@ -284,6 +284,9 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
UserSyncFaceCacheDirectory = "",
SyncPolicies = new UserSyncPoliciesConfig()
};
ApplyDeviceSettingsOverlay(missingCfg, configPath);
NormalizeLoadedDeviceConfig(missingCfg);
return missingCfg;
}
var raw = File.ReadAllText(configPath);
@ -296,6 +299,7 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
{
var cfg = (HikvisionServiceConfig)serializer.ReadObject(ms);
ApplyEnvironmentOverrides(cfg);
ApplyDeviceSettingsOverlay(cfg, configPath);
NormalizeLoadedDeviceConfig(cfg);
return cfg;
}
@ -308,6 +312,7 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
{
var cfg = (HikvisionServiceConfig)serializer.ReadObject(ms);
ApplyEnvironmentOverrides(cfg);
ApplyDeviceSettingsOverlay(cfg, configPath);
NormalizeLoadedDeviceConfig(cfg);
return cfg;
}
@ -315,7 +320,7 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
}
catch
{
return new HikvisionServiceConfig
var fallbackCfg = new HikvisionServiceConfig
{
LogDirectory = @"C:\Users\Public\HikvisionAttendanceService",
SqlConnectionString = "",
@ -362,6 +367,9 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
UserSyncFaceCacheDirectory = "",
SyncPolicies = new UserSyncPoliciesConfig()
};
ApplyDeviceSettingsOverlay(fallbackCfg, configPath);
NormalizeLoadedDeviceConfig(fallbackCfg);
return fallbackCfg;
}
}
@ -481,6 +489,60 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
cfg.DbPort = parsedPort;
}
/// <summary>
/// Overlays device-scope / template-direction settings from DeviceSettings.config
/// (same folder as serviceconfig.json). Property names and behavior are unchanged.
/// </summary>
private static void ApplyDeviceSettingsOverlay(HikvisionServiceConfig cfg, string serviceConfigPath)
{
if (cfg == null)
return;
string dir;
try
{
dir = Path.GetDirectoryName(Path.GetFullPath(serviceConfigPath ?? "")) ?? "";
}
catch
{
dir = "";
}
if (string.IsNullOrWhiteSpace(dir))
dir = AppContext.BaseDirectory;
var path = Path.Combine(dir, "DeviceSettings.config");
if (!File.Exists(path))
return;
try
{
var raw = File.ReadAllText(path);
var serializer = new DataContractJsonSerializer(typeof(DeviceSettingsFile));
DeviceSettingsFile? overlay;
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(raw)))
overlay = serializer.ReadObject(ms) as DeviceSettingsFile;
if (overlay == null)
return;
if (!string.IsNullOrWhiteSpace(overlay.MachineScopeMode))
cfg.MachineScopeMode = overlay.MachineScopeMode;
if (overlay.ScopedMachineIps != null)
cfg.ScopedMachineIps = new List<string>(overlay.ScopedMachineIps);
cfg.EnableTemplateDeviceToDbSync = overlay.EnableTemplateDeviceToDbSync;
cfg.EnableTemplateDbToDeviceSync = overlay.EnableTemplateDbToDeviceSync;
if (overlay.SourceMachineIp != null)
cfg.SourceMachineIp = overlay.SourceMachineIp;
if (overlay.TargetMachineIps != null)
cfg.TargetMachineIps = new List<string>(overlay.TargetMachineIps);
if (overlay.SyncEmployeeIds != null)
cfg.SyncEmployeeIds = new List<string>(overlay.SyncEmployeeIds);
}
catch
{
// Keep serviceconfig / defaults if DeviceSettings.config cannot be parsed.
}
}
private static void NormalizeLoadedDeviceConfig(HikvisionServiceConfig? cfg)
{
if (cfg == null)
@ -524,6 +586,32 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
}
}
/// <summary>Device-scope and template-direction settings loaded from DeviceSettings.config.</summary>
[DataContract]
internal sealed class DeviceSettingsFile
{
[DataMember]
public string MachineScopeMode { get; set; } = "CENTRAL";
[DataMember]
public List<string> ScopedMachineIps { get; set; } = new List<string>();
[DataMember]
public bool EnableTemplateDeviceToDbSync { get; set; }
[DataMember]
public bool EnableTemplateDbToDeviceSync { get; set; }
[DataMember]
public string SourceMachineIp { get; set; } = "";
[DataMember]
public List<string> TargetMachineIps { get; set; } = new List<string>();
[DataMember]
public List<string> SyncEmployeeIds { get; set; } = new List<string>();
}
/// <summary>Policies for multi-device user/face sync (ISAPI). Loaded from service JSON only.</summary>
[DataContract]
internal sealed class UserSyncPoliciesConfig