From 920b8d1e0c608acb831418c0d0554f2a35739e0f Mon Sep 17 00:00:00 2001 From: "mustafa.ahmed" Date: Wed, 5 Aug 2026 15:36:09 +0500 Subject: [PATCH] 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. --- HikvisionAttendanceWindowsService.cs | 92 +++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/HikvisionAttendanceWindowsService.cs b/HikvisionAttendanceWindowsService.cs index f4aebfe..dc4efb5 100644 --- a/HikvisionAttendanceWindowsService.cs +++ b/HikvisionAttendanceWindowsService.cs @@ -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; } + /// + /// Overlays device-scope / template-direction settings from DeviceSettings.config + /// (same folder as serviceconfig.json). Property names and behavior are unchanged. + /// + 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(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(overlay.TargetMachineIps); + if (overlay.SyncEmployeeIds != null) + cfg.SyncEmployeeIds = new List(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 } } + /// Device-scope and template-direction settings loaded from DeviceSettings.config. + [DataContract] + internal sealed class DeviceSettingsFile + { + [DataMember] + public string MachineScopeMode { get; set; } = "CENTRAL"; + + [DataMember] + public List ScopedMachineIps { get; set; } = new List(); + + [DataMember] + public bool EnableTemplateDeviceToDbSync { get; set; } + + [DataMember] + public bool EnableTemplateDbToDeviceSync { get; set; } + + [DataMember] + public string SourceMachineIp { get; set; } = ""; + + [DataMember] + public List TargetMachineIps { get; set; } = new List(); + + [DataMember] + public List SyncEmployeeIds { get; set; } = new List(); + } + /// Policies for multi-device user/face sync (ISAPI). Loaded from service JSON only. [DataContract] internal sealed class UserSyncPoliciesConfig