using DE70_CMDv2; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SafeBagDE70.API; using SafeBagDE70.Model; using Serilog; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.IO; using System.Linq; using System.ServiceProcess; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; namespace SafeBagDE70 { public static class AppData { public static string CDMPort = "COM4"; public static string SensorPort = "COM3"; public static Version? CurrentVersion = null; public static List? customerAccounts = null; public static CustomerAccount? CurrentCustomerAccount{get;set;} public static CCmdRsp? cCmdRsp=null; public static bool IsAuthenticated = false; public static bool IsInTransaction = false; public static int WDTCount = 0; public static ObservableCollection logCollection = new ObservableCollection(); public static ListView logListView = null; public static int MaxLogCount = 500; public static int MaxNotes = 300; public static string BaseURL = ""; public static int TenantId = 0; public static bool IsInternetOk = false; static Task? resendTask = null; static Task? commandTask = null; public static DeviceStatus? _DeviceStatus = null; public static DeviceStatus? DeviceStatus { set { _DeviceStatus = value; } get { if (_DeviceStatus == null) { InitDeviceStatus(); } else { _DeviceStatus.DeviceTime = DateTime.Now; } return _DeviceStatus; } } public static bool bInitUser = false; static AppData() { CDMSTATE = new CDMSTATE(); ADMINDEVICESTATUS = new ADMINDEVICESTATUS(); //InitDeviceStatus(); } public static void Init() { try { bInitUser = InitUsers(); } catch (Exception ex) { Log.Fatal(ex, "AppInit: "+ex.Message); if(DeviceStatus != null) { DeviceStatus.Description = "Exception in AppInit: "+ex.Message; APIModels.SendEvent(DeviceStatus); } } } public static void InitDeviceStatus() { DeviceStatus = new DeviceStatus { Device = Environment.MachineName, TenantId = AppData.TenantId, Status = 0, CDMStatus = 0, PrinterStatus = 0, SensorStatus = 0, Notes = "", OtherError = 0, }; } public static bool InitUsers() { int retryCount = 0; int maxRetries = 20; // Max retries before giving up int delay = 5000; // Delay between retries (5 seconds) while (retryCount < maxRetries) { try { customerAccounts = new List(); using (var context = new CDMDbContext()) { var customers = context.Customers; foreach (var customer in customers) { var ca = new CustomerAccount { Name = customer.TenantName, CustomerId = customer.AccountNumber, QRCode = customer.AccountNumber, PINCode = customer.PinCode }; customerAccounts.Add(ca); } } CurrentCustomerAccount = customerAccounts[0]; Log.Information("InitUsers thành công"); bInitUser = true; break; } catch (Exception ex) { Log.Information($"Retry InitUser: {retryCount}/{maxRetries}"); retryCount++; if (retryCount >= maxRetries) { Log.Fatal(ex, "InitUsers: " + ex.Message); if (DeviceStatus != null) { DeviceStatus.Description = "Exception in InitUsers: " + ex.Message; APIModels.SendEvent(DeviceStatus); } break; } // Wait for the next retry Thread.Sleep(delay); } } return bInitUser; } public static CDMSTATE CDMSTATE; public static ADMINDEVICESTATUS ADMINDEVICESTATUS; private static DateTime CutOffTime = new DateTime(1, 1, 1, 11, 0, 0); // 11:00 a.m as cut-off time public static string GetNumberPartFromMerchantCode(string merchantCode) { if (merchantCode == null) return ""; var match = Regex.Match(merchantCode, @"\d+$"); string merchantCode_part = match.Value; return merchantCode_part; } public static void CreateNewTransactionCode() { DateTime transactionDateTime = DateTime.Now; string merchantCode_part = GetNumberPartFromMerchantCode(CurrentCustomerAccount.CustomerId); // Adjust transaction date if it's past cut-off time (12:00 noon) DateTime prevadjustedDate = CurrentCustomerAccount.TransactionTime.Hour >= CutOffTime.Hour ? transactionDateTime.Date : transactionDateTime.Date.AddDays(-1); DateTime adjustedDate = transactionDateTime.Hour >= CutOffTime.Hour ? transactionDateTime.Date : transactionDateTime.Date.AddDays(-1); if ((adjustedDate - prevadjustedDate).Days > 0) { CurrentCustomerAccount.TransactionCount =1; } else { CurrentCustomerAccount.TransactionCount++; } var transactionCount = CurrentCustomerAccount.TransactionCount; string datePart = transactionDateTime.ToString("yyyyMMdd"); string timePart = transactionDateTime.ToString("HHmmss"); string transactionCountPart = transactionCount.ToString("D2"); // Ensure 2 digits // Combine all parts to form the transaction ID string transactionId = $"{datePart}{timePart}{merchantCode_part}{transactionCountPart}"; CurrentCustomerAccount.TransactionCode = transactionId; CurrentCustomerAccount.TransactionTime = transactionDateTime; } public static string GetTransactionCode() { if (CurrentCustomerAccount != null) return CurrentCustomerAccount.TransactionCode; else return ""; } public static CDMSTATE? GetCDMState() { try { if (cCmdRsp != null) { cCmdRsp.SendDeSense(); string strSend = cCmdRsp.GetSendDat(false); string strRecv = cCmdRsp.GetRecvDat(); Log.Information($"[SEND] {strSend}"); Log.Information($"[RECV] {strRecv}"); var data = cCmdRsp.recv_dat.dat; if (AppData.CDMSTATE != null) { AppData.CDMSTATE.IsCassetteFull = data[17] & 0x08; AppData.CDMSTATE.IsNotesInEscrow = data[4] & 0x01; AppData.CDMSTATE.IsRejectPocketFull = data[3] & 0x40; AppData.CDMSTATE.IsNotesInRejectPocket = data[3] & 0x10; AppData.CDMSTATE.IsNotesInHopper = data[3] & 0x01; AppData.CDMSTATE.IsStackerFull = (data[2] == 0x43?1:0); if (data[0] == 0x40 && (data[1] == 0x07 || data[1]==0x09)) AppData.CDMSTATE.IsError = 0; else AppData.CDMSTATE.IsError = 1; AppData.CDMSTATE.RequestTime = DateTime.Now; AppData.CDMSTATE.UpdateCDMStatus(cCmdRsp.recv_dat); } } return AppData.CDMSTATE; } catch (Exception ex) { Log.Fatal(ex, "GetCDMState"); return null; } } public static byte[] GetCassetteData() { cCmdRsp.SendXXCmd((byte)Command.CMD_DE_READ_CASSETTE); var data = cCmdRsp.recv_dat.dat; StringBuilder str = new StringBuilder(); var recv_dat = cCmdRsp.recv_dat; AppData.DeviceStatus.Notes = GetNotesString(recv_dat.dat); str.Append("DATA:"); for (int i = 0; i < recv_dat.leng - 1; i++) { var dat = string.Format("{0:X2} ", recv_dat.dat[i]); str.Append(dat); } Log.Information("[CASSETTE]" + str.ToString()); return (byte[])data.Clone(); } public static string GetNotesString(byte[] cassetData) { int notes = 0; int offset = 14; string str = ""; for (int i = 0; i < 10; i++) { notes = cassetData[2 * i + offset] * 256 + cassetData[2 * i + 1 + offset]; str += notes + ","; } return str.Trim(','); } public static void ResendData() { if (!AppData.IsInTransaction && AppData.IsInternetOk) { if (resendTask == null || resendTask.IsCompleted) { resendTask = Task.Run(async () => { try { using (var context = new CDMDbContext()) { var startTime = DateTime.Now.Date.AddDays(-2).Date; var unsentHist = context.Histories .Where(x => x.DepositTime >= startTime) .Where(x => ((x.IsSent == null || x.IsSent == false))) .ToList(); foreach (var item in unsentHist) { var data2Send = JsonConvert.DeserializeObject(item.Data); var bResult = await APIModels.SendCountingData(data2Send); if (bResult) { item.IsSent = true; context.Update(item); context.SaveChanges(); Log.Information("Resend: " + item.Data + " successfully"); } else { Log.Error("Resend: " + item.Data + " failed"); } } } } catch (Exception ex) { Log.Fatal(ex, "Resend failed"); } }); } else { Log.Information("Previous resend are running"); } } else { Log.Error($"Resend could not start IsInTransaction: {AppData.IsInTransaction} IsInternetOk: {AppData.IsInternetOk}"); } } public static void GetandExecuteCommands() { if (IsInternetOk && (commandTask == null || commandTask.IsCompleted)) { commandTask = Task.Run(async () => { try { var commands= await APIModels.GetCommands(); if(commands != null && commands.Count()>0) { foreach (var command in commands) { bool bResult = false; switch(command.CommandCode) { case 0: break; case 1: Log.Information("Remote Reset CDM Error"); //Restart CDM bResult=ResetCDMError(); break; case 2: Log.Information("Remote Reset CDM Counter"); bResult = ResetCDMCounter(); if (!bResult) { bResult = ResetCDMCounter(); } break; } // await APIModels.UpdateDeviceCommand(command.Id, bResult?1:-1); } } } catch (Exception ex) { Log.Fatal(ex, "GetandExecuteCommands failed"); } }); } else { var str = ""; if (commandTask != null && !commandTask.IsCompleted) str += "Previous command state are running; "; if (!IsInternetOk) str += "No Internet Connection"; Log.Error("GetandExecuteCommands error: "+str); } } public static bool ResetCDMCounter() { bool bResetOk = false; if (AppData.cCmdRsp != null) { Log.Information("[ADMIN] reset counter"); CCmdRsp cCmdRsp = AppData.cCmdRsp; cCmdRsp.SendDeSense(); Thread.Sleep(1000); cCmdRsp.SendXXCmd((byte)Command.CMD_DE_UNLOCK); Thread.Sleep(1000); cCmdRsp.SendDeSense(); Thread.Sleep(1000); //Event fix var recvData = cCmdRsp.recv_dat; if (recvData.dat[14] == recvData.dat[16]) { cCmdRsp.SendLock(); } else { cCmdRsp.SendEventFix(recvData.dat[16]); cCmdRsp.SendLock(); } var currentMode = cCmdRsp.GetLockParam().mode; cCmdRsp.GetLockParam().mode = Mode.MODE_COLLECTION; cCmdRsp.SendLock(); Thread.Sleep(10000); cCmdRsp.SendDeSense(); Thread.Sleep(1000); recvData = cCmdRsp.recv_dat; if (recvData.dat[14] != recvData.dat[16]) { AppData.GetCassetteData(); bResetOk = true; } } return bResetOk; } public static bool ResetCDMError() { var cCmdRsp = AppData.cCmdRsp; bool bSuccess = false; if(cCmdRsp!=null && cCmdRsp.recv_dat!=null) { if(cCmdRsp.recv_dat.dat[1] == 0x1C && cCmdRsp.recv_dat.dat[2] == 0x4A) { cCmdRsp.SendXXCmd((byte)Command.CMD_DE_SENSE); var recvData = cCmdRsp.recv_dat; if (recvData.dat[14] == recvData.dat[16]) { cCmdRsp.SendLock(); } else { cCmdRsp.SendEventFix(recvData.dat[16]); } Thread.Sleep(1000); cCmdRsp.SendXXCmd((byte)Command.CMD_DE_RESET); Thread.Sleep(2000); cCmdRsp.SendXXCmd((byte)Command.CMD_DE_SENSE); if (cCmdRsp.recv_dat.dat[1] == 0x04 && cCmdRsp.recv_dat.dat[2] == 0x41) { bSuccess = true; } } } return bSuccess; } } public class CustomerAccount { public string? Name { get; set; } public string? CustomerId { get; set; } public string? QRCode { get; set; } public string? PINCode { get; set; } public int TransactionCount { get; set; } = 1; public DateTime TransactionTime { get; set; } public string TransactionCode { get; set; } } public class CountSession { public CountSession() { for (int i = 0; i < QuantityDenomi.Count(); i++) { QuantityDenomi[i] = 0; } for (int i = 0; i < SumDenomi.Count(); i++) { SumDenomi[i] = 0; } for (int i = 0; i < TempNotes.Count(); i++) { TempNotes[i] = 0; } } public int[] QuantityDenomi { get; set; } = new int[10]; public int[] SumDenomi { get; set; } = new int[10]; public bool IsStored = false; public int[] TempNotes { get; set; } = new int[5]; } public class CountingData2Send { public string? TransactionCode { get; set; } public List? Sessions { get; set; } public string? UserId { get; set; } public string? MachineId { get; set; } public string? MachineTime { get; set; } public int RejectNotes { get; set; } public int GenuineNotes { get; set; } public int UnfitNotes { get; set; } public int CounterfeitNotes { get; set; } public int SuspendNotes { get; set; } public int CountingState { get; set; }//0-Normal End, 1-Timeout End } public class NotesInSession : INotifyPropertyChanged { private int _GenuineNotes; public int GenuineNotes { get { return _GenuineNotes; } set { if (_GenuineNotes != value) { _GenuineNotes = value; OnPropertyChanged(nameof(GenuineNotes)); } } } private int _RejectNotes; public int RejectNotes { get { return _RejectNotes; } set { if (_RejectNotes != value) { _RejectNotes = value; OnPropertyChanged(nameof(RejectNotes)); } } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } public enum COUNTINGSTATE { NORMAL=0, AUTOEND=1, FULLIFSTORE=2 } public class ADMINDEVICESTATUS : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private string? _PrinterStatus; public string? PrinterStatus { get { return _PrinterStatus; } set { if (_PrinterStatus != value) { _PrinterStatus = value; OnPropertyChanged(nameof(PrinterStatus)); } } } private string? _SensorStatus; public string? SensorStatus { get { return _SensorStatus; } set { if (_SensorStatus != value) { _SensorStatus = value; OnPropertyChanged(nameof(SensorStatus)); } } } private string? _DeviceStatus; public string? DeviceStatus { get { return _DeviceStatus; } set { if (_DeviceStatus != value) { _DeviceStatus = value; OnPropertyChanged(nameof(DeviceStatus)); } } } } public class CDMSTATE : INotifyPropertyChanged { public int IsCassetteFull = 0; public int IsNotesInEscrow = 0; public int IsRejectPocketFull = 0; public int IsNotesInRejectPocket = 0; public int IsNotesInHopper = 0; public int IsStackerFull = 0; public int IsError = 0; public RecvData? RecvDat = null; public byte GetSR1() { if (RecvDat != null) return RecvDat.dat[0]; return 0; } public byte GetSR2() { if (RecvDat != null) return RecvDat.dat[1]; return 0; } public byte GetSR3() { if (RecvDat != null) return RecvDat.dat[2]; return 0; } public string GetSRStatus() { if (RecvDat != null) return "SR1/2/3: " + AppData.cCmdRsp.GetSR1Summary(RecvDat.dat[0]) + ", " + AppData.cCmdRsp.GetSR2Summary(RecvDat.dat[1]) + ", " + AppData.cCmdRsp.GetSR3Summary(RecvDat.dat[2]); else return ""; } public string GetDtatus() { string strStatus = ""; if (RecvDat != null) { if(RecvDat.leng>8) { var dat=RecvDat.dat; strStatus += "D1: "+AppData.cCmdRsp.GetD1Summary(dat[3]); strStatus += "\r\nD2: "+AppData.cCmdRsp.GetD2Summary(dat[4]); strStatus += "\r\nD3: " + AppData.cCmdRsp.GetD3Summary(dat[5]); strStatus += "\r\nD4: " + AppData.cCmdRsp.GetD4Summary(dat[6]); strStatus += "\r\nD5: " + AppData.cCmdRsp.GetD5Summary(dat[7]); strStatus += "\r\nD6: " + AppData.cCmdRsp.GetD6Summary(dat[8]); } } return strStatus; } public string GetSStatus() { string strStatus = ""; if (RecvDat != null) { if (RecvDat.leng > 18) { var dat = RecvDat.dat; strStatus += "S1: "+ AppData.cCmdRsp.GetS1Summary(dat[17]); strStatus += "\r\nS2: " + AppData.cCmdRsp.GetS2Summary(dat[18]); } } return strStatus; } public string GetFullStatus() { string strStatus = ""; strStatus = GetSRStatus(); strStatus += "\r\n"+GetDtatus(); strStatus += "\r\n"+GetSStatus(); return strStatus; } public void UpdateCDMStatus(RecvData recv_data) { RecvDat=recv_data; if (AppData.cCmdRsp != null) { CDMStatus = RequestTime.ToString("yyyy/MM/dd HH:mm:ss ") + GetSRStatus(); if (recv_data.leng > 18) { } } } private string? _CDMStatus; public string? CDMStatus { get { return _CDMStatus; } set { if (_CDMStatus != value) { _CDMStatus = value; OnPropertyChanged(nameof(CDMStatus)); } } } public DateTime RequestTime = DateTime.Now; public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } public class DeviceStatus { public int TenantId { get; set; } public string Device { get; set; } public int Status { get; set; } public DateTime DeviceTime { get; set; } public int CDMStatus { get; set; } public int PrinterStatus { get; set; } public int SensorStatus { get; set; } public int OtherError { get; set; } public string? Notes { get; set; } public string? TransactionCode { get; set; } public string Description { get; set; } } public enum DeviceStatusCode { NORMAL=0, ERROR=1 } public enum PrinterStatusCode { NORMAL = 0, NEARPAPEROUT = 1, PAPEROUT=2, ERROR=3 } public enum SensorStatusCode { NORMAL = 0, ERROR = 4 } public enum CDMStatusCode { NORMAL = 0, DISCONNECT_SERIALPORT=1, } }