HanvonAttendanceService/DeviceSettingsConfig.cs

223 lines
9.0 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace HanvonF710XAttendanceService
{
internal sealed class DeviceSettingsConfig
{
public string Mode { get; private set; }
public int? SiteId { get; private set; }
public List<DeviceRule> Devices { get; } = new List<DeviceRule>();
public TemplateTransferConfig TemplateTransfer { get; private set; }
public static bool TryLoad(string path, out DeviceSettingsConfig config, out string error)
{
config = null;
error = null;
try
{
if (string.IsNullOrWhiteSpace(path))
{
error = "Device settings path is empty.";
return false;
}
if (!File.Exists(path))
{
config = new DeviceSettingsConfig();
return true;
}
var doc = XDocument.Load(path);
var root = doc.Root;
if (root == null || !string.Equals(root.Name.LocalName, "DeviceSettings", StringComparison.OrdinalIgnoreCase))
{
error = $"Invalid device settings root element. Expected <DeviceSettings>, found <{root?.Name.LocalName ?? "null"}>.";
return false;
}
var parsed = new DeviceSettingsConfig
{
Mode = (string)root.Attribute("Mode")
};
var rootSiteIdStr = (string)root.Attribute("SiteId") ?? (string)root.Attribute("SITE_ID") ?? (string)root.Attribute("SiteID");
if (!string.IsNullOrWhiteSpace(rootSiteIdStr) && int.TryParse(rootSiteIdStr.Trim(), out var rootSiteId))
{
parsed.SiteId = rootSiteId;
}
foreach (var deviceEl in root.Elements().Where(e => string.Equals(e.Name.LocalName, "Device", StringComparison.OrdinalIgnoreCase)))
{
var ip = (string)deviceEl.Attribute("IpAddress") ?? (string)deviceEl.Attribute("Ip") ?? "";
var enabledStr = (string)deviceEl.Attribute("Enabled");
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(enabledStr))
{
continue;
}
if (!TryParseBool(enabledStr, out var enabled))
{
continue;
}
int? siteId = null;
var siteIdStr = (string)deviceEl.Attribute("SiteId") ?? (string)deviceEl.Attribute("SITE_ID") ?? (string)deviceEl.Attribute("SiteID");
if (!string.IsNullOrWhiteSpace(siteIdStr) && int.TryParse(siteIdStr.Trim(), out var parsedSiteId))
{
siteId = parsedSiteId;
}
parsed.Devices.Add(new DeviceRule(ip.Trim(), enabled, siteId));
}
// If SiteId is not specified at root, but devices specify a single consistent SiteId, use it.
if (!parsed.SiteId.HasValue)
{
var distinct = parsed.Devices.Where(d => d.SiteId.HasValue).Select(d => d.SiteId.Value).Distinct().ToList();
if (distinct.Count == 1)
{
parsed.SiteId = distinct[0];
}
}
var templateTransferEl = root.Elements().FirstOrDefault(e => string.Equals(e.Name.LocalName, "TemplateTransfer", StringComparison.OrdinalIgnoreCase));
if (templateTransferEl != null)
{
var ttEnabled = TryParseBool((string)templateTransferEl.Attribute("Enabled"), out var tt) && tt;
var ttConfig = new TemplateTransferConfig(ttEnabled);
foreach (var jobEl in templateTransferEl.Elements().Where(e => string.Equals(e.Name.LocalName, "Job", StringComparison.OrdinalIgnoreCase)))
{
var id = (string)jobEl.Attribute("Id") ?? "";
var enabled = TryParseBool((string)jobEl.Attribute("Enabled"), out var jEnabled) && jEnabled;
var sourceIp = (string)jobEl.Attribute("SourceIp") ?? (string)jobEl.Attribute("SourceIP") ?? "";
if (string.IsNullOrWhiteSpace(id))
{
continue;
}
var targets = new List<string>();
foreach (var targetEl in jobEl.Elements().Where(e => string.Equals(e.Name.LocalName, "Target", StringComparison.OrdinalIgnoreCase)))
{
var tip = (string)targetEl.Attribute("Ip") ?? (string)targetEl.Attribute("IpAddress") ?? "";
if (!string.IsNullOrWhiteSpace(tip))
{
targets.Add(tip.Trim());
}
}
// Optional comma-separated list of employee IDs for DB_TO_DEVICE filtering
var empIdsAttr = (string)jobEl.Attribute("EmpIds") ?? (string)jobEl.Attribute("EmpIDs");
List<string> empIds = null;
if (!string.IsNullOrWhiteSpace(empIdsAttr))
{
empIds = empIdsAttr
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(v => v.Trim())
.Where(v => v.Length > 0)
.ToList();
}
var departmentIdsAttr = (string)jobEl.Attribute("DepartmentIds")
?? (string)jobEl.Attribute("DepartmentIDs")
?? (string)jobEl.Attribute("DeptIds");
List<string> departmentIds = null;
if (!string.IsNullOrWhiteSpace(departmentIdsAttr))
{
departmentIds = departmentIdsAttr
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(v => v.Trim())
.Where(v => v.Length > 0)
.ToList();
}
ttConfig.Jobs.Add(new TemplateTransferJob(id.Trim(), enabled, sourceIp.Trim(), targets, empIds, departmentIds));
}
parsed.TemplateTransfer = ttConfig;
}
config = parsed;
return true;
}
catch (Exception ex)
{
error = $"Failed to load device settings: {ex.Message}";
return false;
}
}
public bool HasAnyAllowlistedDeviceIp()
=> Devices.Any(d => d.Enabled);
public HashSet<string> GetAllowlistedIps()
=> new HashSet<string>(Devices.Where(d => d.Enabled).Select(d => d.IpAddress), StringComparer.OrdinalIgnoreCase);
public HashSet<string> GetBlocklistedIps()
=> new HashSet<string>(Devices.Where(d => !d.Enabled).Select(d => d.IpAddress), StringComparer.OrdinalIgnoreCase);
private static bool TryParseBool(string value, out bool result)
{
if (string.IsNullOrWhiteSpace(value))
{
result = false;
return false;
}
return bool.TryParse(value.Trim(), out result);
}
}
internal sealed class DeviceRule
{
public DeviceRule(string ipAddress, bool enabled, int? siteId)
{
IpAddress = ipAddress ?? "";
Enabled = enabled;
SiteId = siteId;
}
public string IpAddress { get; }
public bool Enabled { get; }
public int? SiteId { get; }
}
internal sealed class TemplateTransferConfig
{
public TemplateTransferConfig(bool enabled)
{
Enabled = enabled;
}
public bool Enabled { get; }
public List<TemplateTransferJob> Jobs { get; } = new List<TemplateTransferJob>();
}
internal sealed class TemplateTransferJob
{
public TemplateTransferJob(string id, bool enabled, string sourceIp, List<string> targetIps, List<string> empIds, List<string> departmentIds = null)
{
Id = id ?? "";
Enabled = enabled;
SourceIp = sourceIp ?? "";
TargetIps = targetIps ?? new List<string>();
EmpIds = empIds ?? new List<string>();
DepartmentIds = departmentIds ?? new List<string>();
}
public string Id { get; }
public bool Enabled { get; }
public string SourceIp { get; }
public List<string> TargetIps { get; }
public List<string> EmpIds { get; }
public List<string> DepartmentIds { get; }
}
}