feat: wire console diagnostics, attendance sync, and delete orchestration
--test-planning / diagnose / inspect; attendance parse logs; delete sync entrypoints.main
parent
d7b8a4057d
commit
6b2d980c8a
423
Program.cs
423
Program.cs
|
|
@ -143,11 +143,10 @@ namespace HanvonF710XAttendanceService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
|
ApplicationPaths.Initialize();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SetDllDirectory(baseDir);
|
SetDllDirectory(ApplicationPaths.BaseDirectory);
|
||||||
Environment.CurrentDirectory = baseDir;
|
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
|
|
@ -200,8 +199,58 @@ namespace HanvonF710XAttendanceService
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetArgValue(string[] args, params string[] keys)
|
||||||
|
{
|
||||||
|
if (args == null || keys == null) return null;
|
||||||
|
foreach (var arg in args)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(arg)) continue;
|
||||||
|
foreach (var key in keys)
|
||||||
|
{
|
||||||
|
if (arg.StartsWith(key + "=", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return arg.Substring(key.Length + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static void RunAsConsole(string[] args)
|
private static void RunAsConsole(string[] args)
|
||||||
{
|
{
|
||||||
|
if (HasArg(args, "--test-planning", "/test-planning"))
|
||||||
|
{
|
||||||
|
int failed = DbToDevicePlanningTests.RunAll();
|
||||||
|
failed += NedoTemplateConverterTests.RunAll();
|
||||||
|
failed += HrmsEmployeePhotoClientTests.RunAll();
|
||||||
|
failed += HrmsEmployeeMappingTests.RunAll();
|
||||||
|
failed += DbToDeviceSummaryTests.RunAll();
|
||||||
|
failed += DbToDeviceFaceErrorsTests.RunAll();
|
||||||
|
failed += MachineUserDeleteSyncTests.RunAll();
|
||||||
|
Environment.ExitCode = failed == 0 ? 0 : 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasArg(args, "--test-template-load", "/test-template-load"))
|
||||||
|
{
|
||||||
|
RunTemplateLoadTest();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string diagnoseEmp = GetArgValue(args, "--diagnose-emp", "/diagnose-emp");
|
||||||
|
if (!string.IsNullOrWhiteSpace(diagnoseEmp))
|
||||||
|
{
|
||||||
|
RunDiagnoseEmployee(diagnoseEmp.Trim());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string inspectPortalPhoto = GetArgValue(args, "--inspect-portal-photo", "/inspect-portal-photo");
|
||||||
|
if (!string.IsNullOrWhiteSpace(inspectPortalPhoto))
|
||||||
|
{
|
||||||
|
RunInspectPortalPhoto(inspectPortalPhoto.Trim());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IsConsoleMode = true;
|
IsConsoleMode = true;
|
||||||
try { AllocConsole(); } catch { }
|
try { AllocConsole(); } catch { }
|
||||||
|
|
||||||
|
|
@ -264,12 +313,7 @@ namespace HanvonF710XAttendanceService
|
||||||
|
|
||||||
public static void WriteToFile(string Message)
|
public static void WriteToFile(string Message)
|
||||||
{
|
{
|
||||||
string path = AppDomain.CurrentDomain.BaseDirectory + "\\TemplateRawLogs";
|
string filepath = ApplicationPaths.DatedFile(ApplicationPaths.TemplateRawLogs, "ServiceLog");
|
||||||
if (!Directory.Exists(path))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(path);
|
|
||||||
}
|
|
||||||
string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\TemplateRawLogs\\ServiceLog_" + DateTime.Now.Date.ToShortDateString().Replace('/', '_') + ".txt";
|
|
||||||
LogService.EnqueueLine(filepath, Message);
|
LogService.EnqueueLine(filepath, Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,7 +321,7 @@ namespace HanvonF710XAttendanceService
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\InternalLogs\\InternalLog_" + DateTime.Now.Date.ToShortDateString().Replace('/', '_') + ".txt";
|
string filepath = ApplicationPaths.DatedFile(ApplicationPaths.InternalLogs, "InternalLog");
|
||||||
LogService.EnqueueLine(filepath, message);
|
LogService.EnqueueLine(filepath, message);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|
@ -290,7 +334,7 @@ namespace HanvonF710XAttendanceService
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\InternalLogs\\UnreachableMachines_" + DateTime.Now.Date.ToShortDateString().Replace('/', '_') + ".txt";
|
string filepath = ApplicationPaths.DatedFile(ApplicationPaths.InternalLogs, "UnreachableMachines");
|
||||||
LogService.EnqueueLine(filepath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}\tmachine_id={machine.MachineId}\tip={machine.MachineIp}\tsite_id={machine.SiteId}\treason={reason}");
|
LogService.EnqueueLine(filepath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}\tmachine_id={machine.MachineId}\tip={machine.MachineIp}\tsite_id={machine.SiteId}\treason={reason}");
|
||||||
|
|
||||||
lock (_unreachableLock)
|
lock (_unreachableLock)
|
||||||
|
|
@ -326,7 +370,7 @@ namespace HanvonF710XAttendanceService
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\InternalLogs\\UnreachableMachines_" + DateTime.Now.Date.ToShortDateString().Replace('/', '_') + ".txt";
|
string filepath = ApplicationPaths.DatedFile(ApplicationPaths.InternalLogs, "UnreachableMachines");
|
||||||
LogService.EnqueueLine(filepath, "");
|
LogService.EnqueueLine(filepath, "");
|
||||||
LogService.EnqueueLine(filepath, "---");
|
LogService.EnqueueLine(filepath, "---");
|
||||||
LogService.EnqueueLine(filepath, "");
|
LogService.EnqueueLine(filepath, "");
|
||||||
|
|
@ -420,7 +464,15 @@ namespace HanvonF710XAttendanceService
|
||||||
var parsedRows = new List<Attendance>();
|
var parsedRows = new List<Attendance>();
|
||||||
foreach (var log in logs)
|
foreach (var log in logs)
|
||||||
{
|
{
|
||||||
parsedRows.Add(new Attendance(log.AcNo, log.CheckTime, false, machine.MachineId, "1", machine.MachineIp, DateTime.Now));
|
string acNo = !string.IsNullOrWhiteSpace(log.EnrollId) ? log.EnrollId.Trim() : (log.AcNo ?? "").Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(acNo))
|
||||||
|
{
|
||||||
|
WriteInternalLog($"[AttendanceSkip] machine_id={machine.MachineId} ip={machine.MachineIp} reason=missing_enrollid name={log.Name ?? ""} time={log.CheckTime:yyyy-MM-dd HH:mm:ss}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteInternalLog($"[AttendanceParsed] machine_id={machine.MachineId} ip={machine.MachineIp} enrollid={acNo} name={log.Name ?? ""} checktime={log.CheckTime:yyyy-MM-dd HH:mm:ss}");
|
||||||
|
parsedRows.Add(new Attendance(acNo, log.CheckTime, false, machine.MachineId, "1", machine.MachineIp, DateTime.Now));
|
||||||
}
|
}
|
||||||
|
|
||||||
int totalFetched = parsedRows.Count;
|
int totalFetched = parsedRows.Count;
|
||||||
|
|
@ -619,31 +671,42 @@ namespace HanvonF710XAttendanceService
|
||||||
var toDelete = attendanceMachineUsersDAO.GetDeletionRequestedSerials(conn, machine.MachineId);
|
var toDelete = attendanceMachineUsersDAO.GetDeletionRequestedSerials(conn, machine.MachineId);
|
||||||
if (toDelete.Count > 0)
|
if (toDelete.Count > 0)
|
||||||
{
|
{
|
||||||
// Map serials to device enroll ids when possible.
|
var pendingItems = new List<MachineUserDeleteItem>();
|
||||||
var deleteIds = new List<string>();
|
|
||||||
foreach (var empId in toDelete)
|
foreach (var empId in toDelete)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(empId)) continue;
|
if (string.IsNullOrWhiteSpace(empId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string trimmed = empId.Trim();
|
||||||
var match = users.FirstOrDefault(u =>
|
var match = users.FirstOrDefault(u =>
|
||||||
string.Equals(u.SerialNumber, empId.Trim(), StringComparison.OrdinalIgnoreCase)
|
string.Equals(u.SerialNumber, trimmed, StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(u.DeviceUserId, empId.Trim(), StringComparison.OrdinalIgnoreCase)
|
|| string.Equals(u.DeviceUserId, trimmed, StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(u.Name, empId.Trim(), StringComparison.OrdinalIgnoreCase));
|
|| string.Equals(u.Name, trimmed, StringComparison.OrdinalIgnoreCase));
|
||||||
deleteIds.Add(match != null ? match.DeviceUserId : empId.Trim());
|
string deviceEnrollId = match != null ? match.DeviceUserId : trimmed;
|
||||||
|
pendingItems.Add(new MachineUserDeleteItem(trimmed, deviceEnrollId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client.DeleteUsers(deleteIds, out string delErr))
|
int batchSize = MachineUserDeleteSettings.GetBatchSize();
|
||||||
{
|
var deleteResult = MachineUserDeleteSync.Execute(
|
||||||
foreach (var empId in toDelete)
|
machine.MachineId,
|
||||||
|
machine.MachineIp,
|
||||||
|
pendingItems,
|
||||||
|
batchSize,
|
||||||
|
(IReadOnlyList<string> deviceIds, out string delErr) => client.DeleteUsers(deviceIds, out delErr),
|
||||||
|
serial =>
|
||||||
{
|
{
|
||||||
attendanceMachineUsersDAO.MarkUserDeleted(conn, machine.MachineId, empId);
|
attendanceMachineUsersDAO.MarkUserDeleted(conn, machine.MachineId, serial);
|
||||||
string line = empId + " removed from " + machine.MachineId;
|
string line = serial + " removed from " + machine.MachineId;
|
||||||
responses.Add(line);
|
responses.Add(line);
|
||||||
WriteInternalLog(line);
|
WriteInternalLog(line);
|
||||||
}
|
},
|
||||||
}
|
WriteInternalLog);
|
||||||
else
|
|
||||||
|
if (deleteResult.FailedCount > 0)
|
||||||
{
|
{
|
||||||
WriteInternalLog("syncMachineUsers HTTP deleteusers failed: " + delErr);
|
WriteInternalLog($"syncMachineUsers HTTP deleteusers completed with failures. machine_id={machine.MachineId} success={deleteResult.SuccessCount} failed={deleteResult.FailedCount}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -839,6 +902,304 @@ namespace HanvonF710XAttendanceService
|
||||||
return logs;
|
return logs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void RunTemplateLoadTest()
|
||||||
|
{
|
||||||
|
var logs = new List<string>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
if (!TryLoadDeviceSettings(out var settings, logs))
|
||||||
|
{
|
||||||
|
foreach (var line in logs) Console.WriteLine(line);
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var job = settings?.TemplateTransfer?.Jobs?.FirstOrDefault(j => j.Enabled);
|
||||||
|
if (job == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("No enabled template transfer job found.");
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var conn = OpenDbConnectionWithRetry("testTemplateLoad"))
|
||||||
|
{
|
||||||
|
var employeeIds = DbToDevicePlanning.ResolveEmployeeIds(
|
||||||
|
job,
|
||||||
|
departmentIds => new AttendanceMachineFaceTemplateDAO().GetActiveSerialNumbersByDepartmentIds(conn, departmentIds, new List<string>()),
|
||||||
|
() => new List<string>(job.EmpIds ?? new List<string>()));
|
||||||
|
|
||||||
|
Console.WriteLine($"[DB_TO_DEVICE] Template load test employees={employeeIds.Count}");
|
||||||
|
var runner = new TemplateTransferRunner(new AttendanceMachineDAO(), new AttendanceMachineFaceTemplateDAO());
|
||||||
|
var templates = runner.LoadTemplatesForDbToDeviceTest(conn, employeeIds, logs);
|
||||||
|
int missing = employeeIds.Count - templates.Count;
|
||||||
|
logs.Add($"[DB_TO_DEVICE] Template loading complete requested={employeeIds.Count} loaded={templates.Count} missing={missing}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logs.Add("Template load test failed: " + ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var line in logs)
|
||||||
|
{
|
||||||
|
Console.WriteLine(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
Environment.ExitCode = logs.Any(l => l.IndexOf("failed", StringComparison.OrdinalIgnoreCase) >= 0) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunInspectPortalPhoto(string deviceSerial)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
string photoEmployeeId = deviceSerial;
|
||||||
|
string mappedName = null;
|
||||||
|
using (var conn = OpenDbConnectionWithRetry("inspectPortalPhoto"))
|
||||||
|
{
|
||||||
|
var employeeDao = new HrmsEmployeeDAO();
|
||||||
|
if (employeeDao.TryGetByDeviceSerial(conn, deviceSerial, out HrmsEmployeeInfo employee))
|
||||||
|
{
|
||||||
|
photoEmployeeId = employee.EmployeeId;
|
||||||
|
mappedName = employee.ConcatenatedName;
|
||||||
|
Console.WriteLine($"[PORTAL_PHOTO] device_serial={deviceSerial} hrms_id={employee.EmployeeId} name={employee.ConcatenatedName}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[PORTAL_PHOTO] device_serial={deviceSerial} hrms_lookup=MISS (treating argument as employees.id for photo URL)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string url = HrmsEmployeePhotoClient.BuildPhotoUrl(EmployeePhotoSourceSettings.GetBaseUrl(), photoEmployeeId);
|
||||||
|
Console.WriteLine($"[PORTAL_PHOTO] emp={deviceSerial} hrms_id={photoEmployeeId} enabled={EmployeePhotoSourceSettings.IsEnabled()} url={url}");
|
||||||
|
|
||||||
|
if (!HrmsEmployeePhotoClient.TryDownloadEmployeePhoto(photoEmployeeId, out HrmsEmployeePhotoDownloadResult result))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[PORTAL_PHOTO] emp={deviceSerial} hrms_id={photoEmployeeId} download=FAILED http={result?.StatusCode ?? 0} content_type={result?.ContentType ?? ""} err={result?.Error ?? "unknown"}");
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"[PORTAL_PHOTO] emp={deviceSerial} hrms_id={photoEmployeeId} name={mappedName ?? ""} download=OK http={result.StatusCode} content_type={result.ContentType ?? ""}");
|
||||||
|
Console.WriteLine($"[PORTAL_PHOTO] emp={deviceSerial} photo_size={result.ImageBytes?.Length ?? 0} width={result.Width} height={result.Height} base64_len={result.Base64Length}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Inspect portal photo failed: " + ex);
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunDiagnoseEmployee(string empId)
|
||||||
|
{
|
||||||
|
var logs = new List<string>();
|
||||||
|
var writer = new StringWriter();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
if (!TryLoadDeviceSettings(out var settings, logs))
|
||||||
|
{
|
||||||
|
foreach (var line in logs) Console.WriteLine(line);
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var job = settings?.TemplateTransfer?.Jobs?.FirstOrDefault(j => j.Enabled);
|
||||||
|
if (job == null || job.TargetIps == null || job.TargetIps.Count == 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("No enabled template job with targets found.");
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string targetIp = job.TargetIps[0];
|
||||||
|
using (var conn = OpenDbConnectionWithRetry("diagnoseEmp"))
|
||||||
|
{
|
||||||
|
var machineDao = new AttendanceMachineDAO();
|
||||||
|
if (!machineDao.TryGetAttendanceMachineByIp(conn, targetIp, out var machine, out _))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Target machine not found for ip=" + targetIp);
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var templateDao = new AttendanceMachineFaceTemplateDAO();
|
||||||
|
var templates = templateDao.GetActiveTemplatesBySerialNos(conn, new List<string> { empId });
|
||||||
|
byte[] blob = null;
|
||||||
|
if (!templates.TryGetValue(empId, out blob))
|
||||||
|
{
|
||||||
|
if (!EmployeePhotoSourceSettings.IsEnabled())
|
||||||
|
{
|
||||||
|
Console.WriteLine("No active template in DB for emp=" + empId);
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
blob = Array.Empty<byte>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var uploadPlan = DbToDeviceUploadPlan.FromBlob(blob);
|
||||||
|
if (uploadPlan.Format == DbToDeviceTemplateFormat.Empty && EmployeePhotoSourceSettings.IsEnabled())
|
||||||
|
{
|
||||||
|
uploadPlan.Format = DbToDeviceTemplateFormat.NedoXml;
|
||||||
|
uploadPlan.PushMode = "face";
|
||||||
|
uploadPlan.UseProfileVerification = true;
|
||||||
|
}
|
||||||
|
bool portalPhotoEnabled = uploadPlan.Format == DbToDeviceTemplateFormat.NedoXml
|
||||||
|
&& EmployeePhotoSourceSettings.IsEnabled();
|
||||||
|
if (!uploadPlan.HasUploadPayload && !portalPhotoEnabled)
|
||||||
|
{
|
||||||
|
Console.WriteLine("No uploadable template payload for emp=" + empId + " format=" + uploadPlan.FormatLabel);
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var machineUserDao = new AttendanceMachineUserDAO();
|
||||||
|
var employeeDao = new HrmsEmployeeDAO();
|
||||||
|
HrmsEmployeeInfo hrmsEmployee = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
employeeDao.TryGetByDeviceSerial(conn, empId, out hrmsEmployee);
|
||||||
|
}
|
||||||
|
catch (Exception exEmp)
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} hrms_lookup=FAILED err={exEmp.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
string machineUserName = machineUserDao.GetEmployeeNameForMachine(conn, machine.MachineId, empId);
|
||||||
|
string displayName = !string.IsNullOrWhiteSpace(hrmsEmployee?.ConcatenatedName)
|
||||||
|
? hrmsEmployee.ConcatenatedName
|
||||||
|
: (!string.IsNullOrWhiteSpace(machineUserName) ? machineUserName : empId);
|
||||||
|
|
||||||
|
if (hrmsEmployee != null)
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} hrms_id={hrmsEmployee.EmployeeId} name={displayName}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} hrms_lookup=MISS name={displayName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!int.TryParse(empId, out int enrollId))
|
||||||
|
{
|
||||||
|
Console.WriteLine("empId must be numeric enrollid for Hanvon HTTP API. emp=" + empId);
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var client = HanvonHttpApiClient.ForMachine(machine);
|
||||||
|
if (!client.TryLogin(out string loginErr))
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} login=FAILED err={loginErr}");
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} template=FOUND format={uploadPlan.FormatLabel}");
|
||||||
|
if (uploadPlan.Format == DbToDeviceTemplateFormat.NedoXml)
|
||||||
|
{
|
||||||
|
bool hadNedoPayload = uploadPlan.HasUploadPayload;
|
||||||
|
if (portalPhotoEnabled)
|
||||||
|
{
|
||||||
|
string photoEmployeeId = hrmsEmployee?.EmployeeId;
|
||||||
|
if (uploadPlan.TryResolveEmployeePhoto(photoEmployeeId, out string resolveErr))
|
||||||
|
{
|
||||||
|
if (string.Equals(uploadPlan.PhotoSourceLabel, "HRMS_PORTAL", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} hrms_id={photoEmployeeId} photo_source=HRMS_PORTAL photo_url={uploadPlan.PhotoSourceUrl} photo_size={uploadPlan.PhotoByteLength} width={uploadPlan.PreparedWidth} height={uploadPlan.PreparedHeight}");
|
||||||
|
}
|
||||||
|
else if (hadNedoPayload)
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} photo_source=HRMS_PORTAL FAILED err={resolveErr ?? "unknown"}");
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} photo_source=NEDO_XML FALLBACK");
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} nedo_photo=FOUND base64_len={uploadPlan.PhotoBase64Length} size={uploadPlan.PhotoByteLength} blob_len={uploadPlan.SourceBlobLength}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} photo_source=HRMS_PORTAL FAILED err={resolveErr ?? "unknown"}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (uploadPlan.HasUploadPayload)
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} nedo_photo=FOUND base64_len={uploadPlan.PhotoBase64Length} size={uploadPlan.PhotoByteLength} blob_len={uploadPlan.SourceBlobLength}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!uploadPlan.HasUploadPayload)
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} face_upload=FAILED response=no_photo_available");
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!uploadPlan.TryPrepareFacePayload(out string prepareErr))
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} format={uploadPlan.FormatLabel} photo=INVALID err={prepareErr}");
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(uploadPlan.PrepareNote))
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} nedo_photo=PREPARED note={uploadPlan.PrepareNote} width={uploadPlan.PreparedWidth} height={uploadPlan.PreparedHeight} size={uploadPlan.PhotoByteLength}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.TryUploadDbToDeviceTemplate(enrollId, displayName, uploadPlan, out string uploadErr))
|
||||||
|
{
|
||||||
|
if (DbToDeviceFaceErrors.TryParseDuplicateFace(uploadErr, out string existingDeviceId))
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} face=DUPLICATE existing_device_id={existingDeviceId}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} face_upload=FAILED response={uploadErr}");
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (client.VerifyDbToDeviceTemplate(enrollId, uploadPlan, out string faceflag, out string photourl, out string verifyErr))
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} target={machine.MachineIp} face_upload=OK photo_source={uploadPlan.PhotoSourceLabel ?? uploadPlan.FormatLabel} push_mode={uploadPlan.PushMode} payload_len={uploadPlan.Payload?.Length ?? 0}");
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} verify faceflag={faceflag ?? ""} photourl={photourl ?? ""} face=OK");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] employee={empId} verify=FAILED faceflag={faceflag ?? ""} photourl={photourl ?? ""} err={verifyErr}");
|
||||||
|
Environment.ExitCode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var users = client.GetUserList(out string listErr);
|
||||||
|
var match = users.FirstOrDefault(u =>
|
||||||
|
string.Equals(u.DeviceUserId, empId, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(u.SerialNumber, empId, StringComparison.OrdinalIgnoreCase));
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] emp={empId} getuserlist user_exists={(match != null ? "true" : "false")} list_id={match?.DeviceUserId ?? ""} list_name={match?.Name ?? ""}");
|
||||||
|
if (!string.IsNullOrWhiteSpace(listErr))
|
||||||
|
{
|
||||||
|
writer.WriteLine($"[DB_TO_DEVICE] getuserlist_err={listErr}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
writer.WriteLine("Diagnose failed: " + ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
string output = writer.ToString();
|
||||||
|
Console.WriteLine(output);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string logPath = Path.Combine(ApplicationPaths.InternalLogs, "DbToDeviceDiag_" + empId + "_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".txt");
|
||||||
|
File.WriteAllText(logPath, output);
|
||||||
|
Console.WriteLine("Diagnostic log written: " + logPath);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore file write failures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ApplyDeviceSelectionRulesIfConfigured removed: selection is now config-driven (IP allowlist/blocklist) without SITE_ID env var.
|
// ApplyDeviceSelectionRulesIfConfigured removed: selection is now config-driven (IP allowlist/blocklist) without SITE_ID env var.
|
||||||
|
|
||||||
private static bool TryLoadDeviceSettings(out DeviceSettingsConfig settings, List<string> logs)
|
private static bool TryLoadDeviceSettings(out DeviceSettingsConfig settings, List<string> logs)
|
||||||
|
|
@ -848,9 +1209,11 @@ namespace HanvonF710XAttendanceService
|
||||||
string path = ConfigurationManager.AppSettings["DEVICE_SETTINGS_PATH"];
|
string path = ConfigurationManager.AppSettings["DEVICE_SETTINGS_PATH"];
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
{
|
{
|
||||||
path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DeviceSettings.xml");
|
path = "DeviceSettings.xml";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path = ApplicationPaths.Resolve(path);
|
||||||
|
|
||||||
if (!DeviceSettingsConfig.TryLoad(path, out settings, out var error))
|
if (!DeviceSettingsConfig.TryLoad(path, out settings, out var error))
|
||||||
{
|
{
|
||||||
logs?.Add(error);
|
logs?.Add(error);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue