HanvonAttendanceService/HanvonF710XWindowsService.cs

531 lines
19 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Configuration;
namespace HanvonF710XAttendanceService
{
public partial class HanvonF710XWindowsService : ServiceBase
{
private System.Timers.Timer timer = new System.Timers.Timer();
private System.Timers.Timer timer1 = new System.Timers.Timer();
private static readonly object _attendanceLock = new object();
private static readonly object _templateLock = new object();
private static volatile bool _attendanceJobRunning;
private static volatile bool _templateJobRunning;
private volatile bool _stopping;
public HanvonF710XWindowsService()
{
InitializeComponent();
}
public void StartForConsole(string[] args)
{
OnStart(args ?? Array.Empty<string>());
}
public void StopForConsole()
{
OnStop();
}
protected override void OnStart(string[] args)
{
ApplicationPaths.Initialize();
try { LogService.Start(); } catch (Exception ex) { Program.WriteInternalLog("LogService.Start failed: " + ex.Message); }
WriteToFile("--Service is started at " + DateTime.Now);
WriteInternalLog("--Service is started at " + DateTime.Now);
if (Program.IsConsoleMode)
{
Console.WriteLine("--Service is started at " + DateTime.Now);
}
int attendanceMinutes = GetIntConfig("ATTENDANCE_JOB_INTERVAL_MINUTES", 20);
int templateMinutes = GetIntConfig("TEMPLATE_JOB_INTERVAL_MINUTES", 240);
double attendanceIntervalMs = Math.Max(1, attendanceMinutes) * 60 * 1000;
double templateIntervalMs = Math.Max(1, templateMinutes) * 60 * 1000;
timer.Elapsed += OnAttendanceElapsed;
timer.Interval = attendanceIntervalMs;
timer.AutoReset = true;
timer.Enabled = true;
timer1.Elapsed += OnTemplateElapsed;
timer1.Interval = templateIntervalMs;
timer1.AutoReset = true;
timer1.Enabled = true;
WriteInternalLog($"Attendance job interval: {attendanceMinutes} minutes");
WriteInternalLog($"Template job interval: {templateMinutes} minutes");
WriteToFile($"Attendance job interval: {attendanceMinutes} minutes");
WriteToFile($"Template job interval: {templateMinutes} minutes");
try
{
WriteInternalLog("--Startup trigger: running attendance job immediately at " + DateTime.Now);
Task.Run(() => RunStartupAttendanceJob());
}
catch (Exception ex)
{
WriteInternalLog("Startup attendance trigger failed: " + ex.Message);
}
try
{
int delaySec = 30;
WriteInternalLog("--Startup trigger: running template job after " + delaySec + "s at " + DateTime.Now);
Task.Run(() => RunStartupTemplateJob(delaySec));
}
catch (Exception ex)
{
WriteInternalLog("Startup template trigger failed: " + ex.Message);
}
}
protected override void OnStop()
{
_stopping = true;
WriteToFile("--Service is stopping at " + DateTime.Now);
WriteInternalLog("--Service is stopping at " + DateTime.Now);
try
{
timer.Enabled = false;
timer1.Enabled = false;
timer.Elapsed -= OnAttendanceElapsed;
timer1.Elapsed -= OnTemplateElapsed;
}
catch (Exception ex)
{
WriteInternalLog("OnStop timer shutdown failed: " + ex.Message);
}
WaitForRunningJobs();
try
{
timer.Dispose();
timer1.Dispose();
}
catch (Exception ex)
{
WriteInternalLog("OnStop timer dispose failed: " + ex.Message);
}
try { LogService.StopAndFlush(TimeSpan.FromSeconds(5)); } catch { }
WriteToFile("--Service is stopped at " + DateTime.Now);
WriteInternalLog("--Service is stopped at " + DateTime.Now);
}
private void WaitForRunningJobs()
{
int waitSeconds = GetIntConfig("SERVICE_SHUTDOWN_WAIT_SECONDS", 300);
if (waitSeconds <= 0)
{
return;
}
var deadline = DateTime.UtcNow.AddSeconds(waitSeconds);
while ((_attendanceJobRunning || _templateJobRunning) && DateTime.UtcNow < deadline)
{
Thread.Sleep(500);
}
if (_attendanceJobRunning || _templateJobRunning)
{
WriteInternalLog($"--Service stop: jobs still running after {waitSeconds}s (attendance={_attendanceJobRunning}, template={_templateJobRunning})");
}
}
private void RunStartupAttendanceJob()
{
try
{
if (_stopping)
{
return;
}
RunAttendanceJob();
}
catch (Exception ex)
{
WriteInternalLog("Startup attendance job error: " + ex);
}
}
private async Task RunStartupTemplateJob(int delaySec)
{
try
{
await Task.Delay(Math.Max(0, delaySec) * 1000).ConfigureAwait(false);
if (_stopping)
{
return;
}
RunTemplateJob();
}
catch (Exception ex)
{
WriteInternalLog("Startup template job error: " + ex);
}
}
private static int GetIntConfig(string key, int defaultValue)
{
try
{
var raw = ConfigurationManager.AppSettings[key];
if (string.IsNullOrWhiteSpace(raw)) return defaultValue;
return int.TryParse(raw, out var v) && v > 0 ? v : defaultValue;
}
catch
{
return defaultValue;
}
}
private void OnAttendanceElapsed(object source, ElapsedEventArgs e)
{
RunAttendanceJob();
}
private void RunAttendanceJob()
{
if (_stopping)
{
WriteInternalLog("--Attendance tick skipped (service stopping) at " + DateTime.Now);
return;
}
if (!Monitor.TryEnter(_attendanceLock))
{
WriteInternalLog("--Attendance tick skipped (previous attendance job still running) at " + DateTime.Now);
return;
}
try
{
if (_attendanceJobRunning)
{
WriteInternalLog("--Attendance tick skipped (previous attendance job still running) at " + DateTime.Now);
return;
}
_attendanceJobRunning = true;
WriteSyncCycleHeader();
Program.BeginSyncCycle();
WriteInternalLog("--Attendance job started at " + DateTime.Now);
WriteAttendanceServiceLog("--Attendance job started at " + DateTime.Now);
try
{
syncMachineUsers();
}
catch (Exception ex)
{
WriteInternalLog("syncMachineUsers error: " + ex);
WriteMachineUserServiceLog("syncMachineUsers error: " + ex.Message);
}
if (_stopping)
{
WriteInternalLog("--Attendance job interrupted (service stopping) before attendance sync at " + DateTime.Now);
return;
}
try
{
syncAttendance();
}
catch (Exception ex)
{
WriteInternalLog("syncAttendance error: " + ex);
WriteAttendanceServiceLog("syncAttendance error: " + ex.Message);
}
WriteInternalLog("--Attendance job finished at " + DateTime.Now);
WriteAttendanceServiceLog("--Attendance job finished at " + DateTime.Now);
}
catch (Exception ex)
{
WriteInternalLog("--Attendance job failed: " + ex);
WriteAttendanceServiceLog("--Attendance job failed: " + ex.Message);
}
finally
{
try { Program.EndSyncCycle(); } catch (Exception ex) { WriteInternalLog("EndSyncCycle error: " + ex.Message); }
_attendanceJobRunning = false;
Monitor.Exit(_attendanceLock);
}
}
private void OnTemplateElapsed(object source, ElapsedEventArgs e)
{
RunTemplateJob();
}
private void RunTemplateJob()
{
if (_stopping)
{
WriteInternalLog("--Template tick skipped (service stopping) at " + DateTime.Now);
return;
}
if (!Monitor.TryEnter(_templateLock))
{
WriteInternalLog("--Template tick skipped (previous template job still running) at " + DateTime.Now);
return;
}
try
{
if (_templateJobRunning)
{
WriteInternalLog("--Template tick skipped (previous template job still running) at " + DateTime.Now);
return;
}
_templateJobRunning = true;
WriteJobHeaderToSimpleLogs("TEMPLATE");
WriteInternalLog("--Template job started at " + DateTime.Now);
WriteSaveFaceTemplateLog("--Template job started at " + DateTime.Now);
WriteFaceTemplateTransferLog("--Template job started at " + DateTime.Now);
try
{
syncTemplatesIfEnabled();
}
catch (Exception ex)
{
WriteInternalLog("syncTemplatesIfEnabled error: " + ex);
WriteFaceTemplateTransferLog("syncTemplatesIfEnabled error: " + ex);
WriteSaveFaceTemplateLog("syncTemplatesIfEnabled error: " + ex);
}
WriteInternalLog("--Template job finished at " + DateTime.Now);
WriteSaveFaceTemplateLog("--Template job finished at " + DateTime.Now);
WriteFaceTemplateTransferLog("--Template job finished at " + DateTime.Now);
}
catch (Exception ex)
{
WriteInternalLog("--Template job failed: " + ex);
WriteFaceTemplateTransferLog("--Template job failed: " + ex.Message);
WriteSaveFaceTemplateLog("--Template job failed: " + ex.Message);
}
finally
{
_templateJobRunning = false;
Monitor.Exit(_templateLock);
}
}
public void syncAttendance()
{
WriteToFile("--Sync Attendance is recall at " + DateTime.Now);
WriteInternalLog("--Sync Attendance is recall at " + DateTime.Now);
WriteAttendanceServiceLog("--Sync Attendance is recall at " + DateTime.Now);
List<string> responses = Program.syncAttendance();
foreach (var response in responses)
{
WriteToFile(response);
WriteInternalLog(response);
if (IsAttendanceSimpleLine(response))
{
WriteAttendanceServiceLog(response);
}
}
}
public void syncMachineUsers()
{
WriteToFile("--Sync Machine users is recall at " + DateTime.Now);
WriteInternalLog("--Sync Machine users is recall at " + DateTime.Now);
WriteMachineUserServiceLog("--Sync Machine users is recall at " + DateTime.Now);
List<string> responses = Program.syncMachineUsers();
foreach (var response in responses)
{
WriteToFile(response);
WriteInternalLog(response);
if (IsMachineUserSimpleLine(response))
{
WriteMachineUserServiceLog(response);
}
}
}
public void syncTemplatesIfEnabled()
{
WriteToFile("--Template sync is recall at " + DateTime.Now);
WriteInternalLog("--Template sync is recall at " + DateTime.Now);
List<string> responses = Program.syncTemplates();
foreach (var response in responses)
{
WriteToFile(response);
WriteInternalLog(response);
if (IsSaveFaceTemplateLine(response))
{
WriteSaveFaceTemplateLog(response);
}
if (IsFaceTransferLine(response))
{
WriteFaceTemplateTransferLog(response);
}
}
}
public void WriteToFile(string Message)
{
string filepath = ApplicationPaths.DatedFile(ApplicationPaths.Logs, "ServiceLog");
LogService.EnqueueLine(filepath, Message);
}
private void WriteSyncCycleHeader()
{
string time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string line1 = "========================================================";
string line2 = "SYNC CYCLE START";
string line3 = "Time: " + time;
string line4 = "=========================";
WriteToFile(line1);
WriteToFile(line2);
WriteToFile(line3);
WriteToFile(line4);
WriteInternalLog(line1);
WriteInternalLog(line2);
WriteInternalLog(line3);
WriteInternalLog(line4);
WriteAttendanceServiceLog(line1);
WriteAttendanceServiceLog(line2);
WriteAttendanceServiceLog(line3);
WriteAttendanceServiceLog(line4);
WriteMachineUserServiceLog(line1);
WriteMachineUserServiceLog(line2);
WriteMachineUserServiceLog(line3);
WriteMachineUserServiceLog(line4);
}
private void WriteJobHeaderToSimpleLogs(string jobName)
{
string time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string line1 = "========================================================";
string line2 = "SYNC CYCLE START";
string line3 = "Time: " + time;
string line4 = "=========================";
string line5 = "JOB: " + jobName;
WriteSaveFaceTemplateLog(line1);
WriteSaveFaceTemplateLog(line2);
WriteSaveFaceTemplateLog(line3);
WriteSaveFaceTemplateLog(line4);
WriteSaveFaceTemplateLog(line5);
WriteFaceTemplateTransferLog(line1);
WriteFaceTemplateTransferLog(line2);
WriteFaceTemplateTransferLog(line3);
WriteFaceTemplateTransferLog(line4);
WriteFaceTemplateTransferLog(line5);
}
private static bool IsAttendanceSimpleLine(string line)
{
if (string.IsNullOrWhiteSpace(line)) return false;
if (line.IndexOf(" has ", StringComparison.OrdinalIgnoreCase) >= 0 &&
line.IndexOf(" records", StringComparison.OrdinalIgnoreCase) >= 0) return true;
if (line.IndexOf(" is not connected", StringComparison.OrdinalIgnoreCase) >= 0) return true;
return false;
}
private static bool IsMachineUserSimpleLine(string line)
{
if (string.IsNullOrWhiteSpace(line)) return false;
if (line.IndexOf(" -> ", StringComparison.OrdinalIgnoreCase) >= 0) return true;
if (line.IndexOf(" removed from ", StringComparison.OrdinalIgnoreCase) >= 0) return true;
if (line.IndexOf(" is not connected", StringComparison.OrdinalIgnoreCase) >= 0) return true;
return false;
}
private static bool IsSaveFaceTemplateLine(string line)
{
if (string.IsNullOrWhiteSpace(line)) return false;
if (line.StartsWith("Serial number--", StringComparison.OrdinalIgnoreCase)) return true;
if (line.IndexOf("SaveTemplateDB", StringComparison.OrdinalIgnoreCase) >= 0) return true;
if (line.IndexOf("DEVICE_TO_DB", StringComparison.OrdinalIgnoreCase) >= 0) return true;
return false;
}
private static bool IsFaceTransferLine(string line)
{
if (string.IsNullOrWhiteSpace(line)) return false;
if (line.IndexOf("DB_TO_DEVICE", StringComparison.OrdinalIgnoreCase) >= 0) return true;
if (line.IndexOf("DEVICE_TO_DEVICE", StringComparison.OrdinalIgnoreCase) >= 0) return true;
if (line.IndexOf("[DB_TO_DEVICE]", StringComparison.OrdinalIgnoreCase) >= 0) return true;
if (line.IndexOf("TemplateDistribution", StringComparison.OrdinalIgnoreCase) >= 0) return true;
if (line.IndexOf("TemplateRegistrationTargets", StringComparison.OrdinalIgnoreCase) >= 0) return true;
if (line.IndexOf("TemplateDist:", StringComparison.OrdinalIgnoreCase) >= 0) return true;
if (line.IndexOf("SetEmployee", StringComparison.OrdinalIgnoreCase) >= 0) return true;
if (line.IndexOf(" is not connected", StringComparison.OrdinalIgnoreCase) >= 0) return true;
return false;
}
private static void AppendLineToDatedLog(string folderName, string filePrefix, string message)
{
string folderPath = Path.Combine(ApplicationPaths.Logs, folderName);
string datePart = DateTime.Now.ToString("yyyy-MM-dd");
string filePath = Path.Combine(folderPath, filePrefix + "_" + datePart + ".txt");
LogService.EnqueueLine(filePath, message);
}
private static void WriteAttendanceServiceLog(string message)
{
AppendLineToDatedLog("Attendance", "AttendanceServiceLogs", message);
}
private static void WriteMachineUserServiceLog(string message)
{
AppendLineToDatedLog("Machine User service", "MachineServiceLogs", message);
}
private static void WriteSaveFaceTemplateLog(string message)
{
AppendLineToDatedLog("SaveFaceTemplate logs", "SaveFaceTemplateLogs", message);
}
private static void WriteFaceTemplateTransferLog(string message)
{
AppendLineToDatedLog("FaceTransferLog", "FaceTransferLogs", message);
}
public void WriteInternalLog(string Message)
{
Program.WriteInternalLog(Message);
}
}
}