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>
|
||||
static void Main(string[] args)
|
||||
{
|
||||
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||
ApplicationPaths.Initialize();
|
||||
try
|
||||
{
|
||||
SetDllDirectory(baseDir);
|
||||
Environment.CurrentDirectory = baseDir;
|
||||
SetDllDirectory(ApplicationPaths.BaseDirectory);
|
||||
}
|
||||
catch { }
|
||||
|
||||
|
|
@ -200,8 +199,58 @@ namespace HanvonF710XAttendanceService
|
|||
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)
|
||||
{
|
||||
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;
|
||||
try { AllocConsole(); } catch { }
|
||||
|
||||
|
|
@ -264,12 +313,7 @@ namespace HanvonF710XAttendanceService
|
|||
|
||||
public static void WriteToFile(string Message)
|
||||
{
|
||||
string path = AppDomain.CurrentDomain.BaseDirectory + "\\TemplateRawLogs";
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\TemplateRawLogs\\ServiceLog_" + DateTime.Now.Date.ToShortDateString().Replace('/', '_') + ".txt";
|
||||
string filepath = ApplicationPaths.DatedFile(ApplicationPaths.TemplateRawLogs, "ServiceLog");
|
||||
LogService.EnqueueLine(filepath, Message);
|
||||
}
|
||||
|
||||
|
|
@ -277,7 +321,7 @@ namespace HanvonF710XAttendanceService
|
|||
{
|
||||
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);
|
||||
}
|
||||
catch
|
||||
|
|
@ -290,7 +334,7 @@ namespace HanvonF710XAttendanceService
|
|||
{
|
||||
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}");
|
||||
|
||||
lock (_unreachableLock)
|
||||
|
|
@ -326,7 +370,7 @@ namespace HanvonF710XAttendanceService
|
|||
|
||||
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, "");
|
||||
|
|
@ -420,7 +464,15 @@ namespace HanvonF710XAttendanceService
|
|||
var parsedRows = new List<Attendance>();
|
||||
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;
|
||||
|
|
@ -619,31 +671,42 @@ namespace HanvonF710XAttendanceService
|
|||
var toDelete = attendanceMachineUsersDAO.GetDeletionRequestedSerials(conn, machine.MachineId);
|
||||
if (toDelete.Count > 0)
|
||||
{
|
||||
// Map serials to device enroll ids when possible.
|
||||
var deleteIds = new List<string>();
|
||||
var pendingItems = new List<MachineUserDeleteItem>();
|
||||
foreach (var empId in toDelete)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(empId)) continue;
|
||||
if (string.IsNullOrWhiteSpace(empId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string trimmed = empId.Trim();
|
||||
var match = users.FirstOrDefault(u =>
|
||||
string.Equals(u.SerialNumber, empId.Trim(), StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(u.DeviceUserId, empId.Trim(), StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(u.Name, empId.Trim(), StringComparison.OrdinalIgnoreCase));
|
||||
deleteIds.Add(match != null ? match.DeviceUserId : empId.Trim());
|
||||
string.Equals(u.SerialNumber, trimmed, StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(u.DeviceUserId, trimmed, StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(u.Name, trimmed, StringComparison.OrdinalIgnoreCase));
|
||||
string deviceEnrollId = match != null ? match.DeviceUserId : trimmed;
|
||||
pendingItems.Add(new MachineUserDeleteItem(trimmed, deviceEnrollId));
|
||||
}
|
||||
|
||||
if (client.DeleteUsers(deleteIds, out string delErr))
|
||||
{
|
||||
foreach (var empId in toDelete)
|
||||
int batchSize = MachineUserDeleteSettings.GetBatchSize();
|
||||
var deleteResult = MachineUserDeleteSync.Execute(
|
||||
machine.MachineId,
|
||||
machine.MachineIp,
|
||||
pendingItems,
|
||||
batchSize,
|
||||
(IReadOnlyList<string> deviceIds, out string delErr) => client.DeleteUsers(deviceIds, out delErr),
|
||||
serial =>
|
||||
{
|
||||
attendanceMachineUsersDAO.MarkUserDeleted(conn, machine.MachineId, empId);
|
||||
string line = empId + " removed from " + machine.MachineId;
|
||||
attendanceMachineUsersDAO.MarkUserDeleted(conn, machine.MachineId, serial);
|
||||
string line = serial + " removed from " + machine.MachineId;
|
||||
responses.Add(line);
|
||||
WriteInternalLog(line);
|
||||
}
|
||||
}
|
||||
else
|
||||
},
|
||||
WriteInternalLog);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
private static bool TryLoadDeviceSettings(out DeviceSettingsConfig settings, List<string> logs)
|
||||
|
|
@ -848,9 +1209,11 @@ namespace HanvonF710XAttendanceService
|
|||
string path = ConfigurationManager.AppSettings["DEVICE_SETTINGS_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))
|
||||
{
|
||||
logs?.Add(error);
|
||||
|
|
|
|||
Loading…
Reference in New Issue