using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using static System.Windows.Forms.VisualStyles.VisualStyleElement; using ComboBox = System.Windows.Forms.ComboBox; using TextBox = System.Windows.Forms.TextBox; namespace Card_Dispenser_Service { public partial class Form1 : Form { /*********************************************************************************************************************/ private readonly Service1 _service; private readonly CardManager _manager = new CardManager(); private System.Windows.Forms.Timer _logRefreshTimer; public Form1(Service1 service) { InitializeComponent(); comboBox6.Items.AddRange(new object[] { "Read", "Dispense", "Recycle", "Return" }); //TODO read from K720 comboBox6.SelectedIndex = 0; _service = service; this.FormClosing += Form1_FormClosing; // Initialize and start the log refresh timer (5000ms = 5 seconds) _logRefreshTimer = new System.Windows.Forms.Timer(); _logRefreshTimer.Interval = 5000; _logRefreshTimer.Tick += LogRefreshTimer_Tick; _logRefreshTimer.Start(); // Trigger an immediate initial load so you don't wait 5 seconds when the form opens LogRefreshTimer_Tick(this, EventArgs.Empty); } private void LogRefreshTimer_Tick(object sender, EventArgs e) { // Make sure your RichTextBox/TextBox is named correctly here (e.g., rtbLogs or txtLogs) string currentLogs = Utils.GetLogContents(); if (rtbLogs.Text != currentLogs) { rtbLogs.Text = currentLogs; // Auto-scroll to the bottom so you always see the latest logs rtbLogs.SelectionStart = rtbLogs.Text.Length; rtbLogs.ScrollToCaret(); } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { if (_service != null) { _service.Stop(); // Safely triggers OnStop() and shuts down your background thread } if (_logRefreshTimer != null) { _logRefreshTimer.Stop(); _logRefreshTimer.Dispose(); } } private void Form1_Load(object sender, EventArgs e) { } private void disconnect_Click(object sender, EventArgs e) { if (K720.Instance.Disconnect()) { button2.Enabled = false; button1.Enabled = true; textBox1.Text = "Connection Terminated"; } } private void connect_Click(object sender, EventArgs e) { try { var fakePayload = new CardTaskResponse { Command = "Connect", Serial = comboBox1.Text, BaudRate = int.Parse(comboBox2.Text) }; dynamic result = _manager.ExecuteHardwareCommand(fakePayload); bool isConected = result.isConnected; if (isConected) { button1.Enabled = false; button2.Enabled = true; textBox1.Text = "Connection Success"; } } catch (Exception ex) { Utils.LogToFile(ex.Message, "connect_Click"); } } private void button3_Click(object sender, EventArgs e) { int nRet; //byte[] cmd = new byte[10]; //cmd[0] = 0x46; //cmd[1] = 0x43; //cmd[2] = 0x37; #if true //nRet = K720_SendCmd(ComHandle, MacAddr, cmd, 3, RecordInfo); nRet = K720.K720_MoveCard(0x30); if (nRet == 0) { textBox1.Text = "Move card to read position success"; } else textBox1.Text = "Move card to read position failed"; #else byte[] DeviceStatus=new byte[4]; byte[] TransportStatus=new byte[4]; byte[] CardBoxStatus=new byte[4]; byte[] RetainBoxStatus=new byte[4]; nRet = K720_CheckCardPosition(DeviceStatus, TransportStatus, CardBoxStatus, RetainBoxStatus); if (nRet == 0) { String str = "", strText = ""; switch (DeviceStatus[0]) { case 0x30: str="machine status: value = 0x30 idle"; break; case 0x31: str="machine status: value = 0x31 fault"; break; case 0x32: str="machine status: value = 0x32 ready card failed"; break; case 0x33: str="machine status: value = 0x33 sending card"; break; case 0x34: str="machine status: value = 0x34 retaining card"; break; case 0x35: str="machine status: value = 0x35 send card failed"; break; case 0x36: str = "machine status: value = 0x36 retain card failed"; break; default: str = "machine status: value = 0x" + DeviceStatus[0].ToString("X2") + " undefine error"; break; } strText += str; strText += "\r\n"; switch (TransportStatus[0]) { case 0x30: str = "channel status: value = 0x30 double card"; break; case 0x31: str = "channel status: value = 0x31 jam"; break; case 0x32: str = "channel status: value = 0x32 read position have card"; break; case 0x33: str = "channel status: value = 0x33 have no card"; break; case 0x34: str = "channel status: value = 0x34 front have card"; break; default: str = "channel status: value = 0x" + TransportStatus[0].ToString("X2") + " undefine error"; break; } strText += str; strText += "\r\n"; switch (CardBoxStatus[0]) { case 0x30: str = "card box status: value = 0x30 empty"; break; case 0x31: str = "card box status: value = 0x31 less"; break; case 0x32: str = "card box status: value = 0x32 more"; break; case 0x33: str = "card box status: value = 0x33 not full"; break; case 0x34: str = "card box status: value = 0x34 full"; break; default: str = "card box status: value = 0x" + CardBoxStatus[0].ToString("X2") + " undefine error"; break; } MessageBox.Show(str); strText += str; strText += "\r\n"; switch (RetainBoxStatus[0]) { case 0x30: str = "retain box status: value = 0x30 not full"; break; case 0x31: str = "retain box status: value = 0x31 full"; break; default: str = "retain box status: value = 0x" + RetainBoxStatus[0].ToString("X2") + " undefine error"; break; } strText += str; strText += "\r\n"; //str += "设备状态:0x" + DeviceStatus[0].ToString("X2") + ",通道状态:0x" + TransportStatus[0].ToString("X2") + ",卡箱状态:0x" + CardBoxStatus[0].ToString("X2") + ",回收箱状态:0x" + RetainBoxStatus[0].ToString("X2"); MessageBox.Show(strText); textBox1.Text = "check success"; } else textBox1.Text = "check failed"; #endif } private void button4_Click(object sender, EventArgs e) { bool nRet; //byte[] cmd = new byte[10]; //cmd[0] = 0x46; //cmd[1] = 0x43; //cmd[2] = 0x34; //nRet = K720_SendCmd(ComHandle, MacAddr, cmd, 3, RecordInfo); nRet = K720.Instance.MoveCardToNonClipPosition(); if (!nRet) { textBox1.Text = "move card to non-clip position success"; } else textBox1.Text = "move card to non-clip position failed"; } private void button5_Click(object sender, EventArgs e) { K720.Instance.RecycleCard(); } private void button6_Click(object sender, EventArgs e) { //int nRet; //byte[] cmd = new byte[10]; //cmd[0] = 0x52; //cmd[1] = 0x53; //nRet = K720_SendCmd(ComHandle, MacAddr, cmd, 2, RecordInfo); //if (nRet == 0) //{ // textBox1.Text = "Reset Success"; //} //else // textBox1.Text = "Reset Failed"; int nRet = 0; byte[] cmd = new byte[3]; byte[] info = new byte[4]; nRet = K720.K720_SensorQuery(K720.Instance.ComHandle, K720.Instance.MacAddr, info, K720.Instance.RecordInfo); if (nRet != 0) { textBox1.Text = "check card position failed"; return; } if ((info[0] & 0x04) == 0x04)//设备故障,需要复位 { cmd[0] = (byte)'R'; cmd[1] = (byte)'S'; nRet = K720.K720_SendCmd(K720.Instance.ComHandle, K720.Instance.MacAddr, cmd, 2, K720.Instance.RecordInfo); if (nRet != 0) { textBox1.Text = "reset failed"; return; } Thread.Sleep(1500); } if ((info[3] & 0x03) != 0x00) { textBox1.Text = "通道有卡,不能执行进卡操作"; return; } cmd[0] = (byte)'F'; cmd[1] = (byte)'C'; cmd[2] = (byte)'8'; nRet = K720.K720_SendCmd(K720.Instance.ComHandle, K720.Instance.MacAddr, cmd, 3, K720.Instance.RecordInfo); if (nRet != 0) { textBox1.Text = "enter card failed"; return; } textBox1.Text = "前端进卡成功"; } private void button7_Click(object sender, EventArgs e) { int nRet; nRet = K720.K720_S50DetectCard(K720.Instance.ComHandle, K720.Instance.MacAddr, K720.Instance.RecordInfo); if (nRet == 0) { textBox1.Text = "Detect Card Success"; } else textBox1.Text = "Detect Card Failed"; } private void button8_Click(object sender, EventArgs e) { int nRet; byte[] CardId = new byte[10]; string str; nRet = K720.K720_S50GetCardID(K720.Instance.ComHandle, K720.Instance.MacAddr, CardId, K720.Instance.RecordInfo); if (nRet == 0) { str = CardId[0].ToString("X2") + " " + CardId[1].ToString("X2") + " " + CardId[2].ToString("X2") + " " + CardId[3].ToString("X2"); MessageBox.Show(str, "Card ID", MessageBoxButtons.OK, MessageBoxIcon.Information); textBox1.Text = "Get Card ID Success"; } else textBox1.Text = "Get Card ID Failed"; } private void button9_Click(object sender, EventArgs e) { int nRet; byte[] ReadData = new byte[20]; string str, str1; nRet = K720.K720_S50ReadBlock(K720.Instance.ComHandle, K720.Instance.MacAddr, (byte)comboBox3.SelectedIndex, (byte)comboBox4.SelectedIndex, ReadData, K720.Instance.RecordInfo); if (nRet == 0) { str = ""; for (int i = 0; i < 16; i++) { str += ReadData[i].ToString("X2"); } textBox3.Text = str; textBox1.Text = "Read Data Success"; } else { // "X2" formats it as uppercase Hex with at least 2 digits (e.g., 0x0A) // You can use just "X" if you don't care about leading zeros. String message = "Read Data Failed 0x" + nRet.ToString("X2"); textBox1.Text = message; } //int nRet; //byte[] WriteData = new byte[16]; //string str; //str = textBox3.Text; //if(str.Length > 32) //{ // MessageBox.Show("长度太长"); // return; //} //else if(str.Length < 32) //{ // MessageBox.Show("长度太短"); // return; //} //StringToArray(str, WriteData); //nRet = K720_S50WriteBlock(ComHandle, MacAddr, (byte)comboBox3.SelectedIndex, (byte)comboBox4.SelectedIndex, WriteData, RecordInfo); //if(nRet == 0) //{ // textBox1.Text = "Write Data Success"; //} //else //{ // textBox1.Text = "Write Data Failed"; //} } private String hexTostr(byte[] data, int length) { String str = ""; for (int i = 0; i < length; i++) { str += data[i].ToString("X2"); } return str; } public byte CharToHex(char ch) { if (ch >= 48 && ch <= 57) { return (byte)((byte)ch - 48); } else if (ch >= 65 && ch <= 70) { return (byte)((byte)ch - 65 + 10); } else if (ch >= 97 && ch <= 102) { return (byte)((byte)ch - 65 + 10); } else return 0; } public void StringToArray(string str, byte[] recv) { if (str.Length % 2 == 1) str = "0" + str; for (int i = 0; i < str.Length / 2; i++) { recv[i] = (byte)((byte)CharToHex(str[2 * i]) * 16 + (byte)CharToHex(str[2 * i + 1])); } } private void button10_Click(object sender, EventArgs e) { string strpassword; int nRet; byte[] Password = new byte[10]; strpassword = textBox2.Text; if (strpassword.Length != 12) { MessageBox.Show("PassWord length is not six byte hex", "Message", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } StringToArray(strpassword, Password); nRet = K720.K720_S50LoadSecKey(K720.Instance.ComHandle, K720.Instance.MacAddr, (byte)comboBox3.SelectedIndex, (byte)(comboBox5.SelectedIndex + 0x30), Password, K720.Instance.RecordInfo); if (nRet == 0) { textBox1.Text = "Verify Password Success"; } else textBox1.Text = "Verify Password Failed"; } private void groupBox5_Enter(object sender, EventArgs e) { } private void groupBox12_Enter(object sender, EventArgs e) { } static String command = "NOP"; private void button26_Click(object sender, EventArgs e) { if (command == "Dispense") { K720.Instance.SendCmd(K720.CardCommand.Dispense); } if (command == "Recycle") { K720.Instance.SendCmd(K720.CardCommand.Recycle); } if (command == "Read") { K720.Instance.SendCmd(K720.CardCommand.Read); } if (command == "Return") { K720.Instance.SendCmd(K720.CardCommand.Return); } } // Maps the user-friendly dropdown selection to the actual command code private void textBox17_TextChanged(object sender, EventArgs e) { TextBox textBox = sender as TextBox; if (textBox != null) { command = textBox.Text; Console.WriteLine(command); } } private void btnWrite_Click(object sender, EventArgs e) { byte sectorAddr = (byte)comboBox3.SelectedIndex; byte blockAddr = (byte)comboBox4.SelectedIndex; // 1. Hard Block: Prevent writing to the Manufacturer Block (Sector 0, Block 0) if (sectorAddr == 0 && blockAddr == 0) { textBox1.Text = "Write Error: Cannot write to Sector 0, Block 0 (Manufacturer/UID block)."; return; } // 2. Warning Check: If user selects Block 3 (Sector Trailer) if (blockAddr == 3) { string warnMsg = $"Warning: You are attempting to write to Sector {sectorAddr}, Block 3.\n\n" + "This is the Sector Trailer. Writing data here overwrites your access keys. " + "If the formatting is wrong, this sector will be permanently locked.\n\n" + "Do you want to proceed?"; DialogResult result = MessageBox.Show(warnMsg, "Critical Security Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (result == DialogResult.No) { textBox1.Text = "Write canceled by user (Protected Block 3)."; return; } } byte keyType = 0x30; // Key A (Try switching to 0x31 for Key B if Key A fails) byte[] keyData = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // Prepare 16 bytes of data to write from textBox18 byte[] writeData = new byte[16]; string inputHex = textBox3.Text.Trim(); for (int i = 0; i < 16; i++) { if (i * 2 + 2 <= inputHex.Length) { writeData[i] = Convert.ToByte(inputHex.Substring(i * 2, 2), 16); } else { writeData[i] = 0x00; } } Task.Run(() => { int nRet; char[] recordInfo = new char[256]; // 1. Detect Card nRet = K720.K720_S50DetectCard(K720.Instance.ComHandle, K720.Instance.MacAddr, recordInfo); if (nRet != 0) { Invoke(new Action(() => { textBox1.Text = "Card Detection Failed 0x" + nRet.ToString("X2"); })); return; } // 2. Authenticate Key immediately prior to writing nRet = K720.K720_S50LoadSecKey(K720.Instance.ComHandle, K720.Instance.MacAddr, sectorAddr, keyType, keyData, recordInfo); if (nRet != 0) { Invoke(new Action(() => { textBox1.Text = "Key Authentication Failed 0x" + nRet.ToString("X2"); })); return; } // 3. Execute Write Block nRet = K720.K720_S50WriteBlock(K720.Instance.ComHandle, K720.Instance.MacAddr, sectorAddr, blockAddr, writeData, recordInfo); if (nRet == 0) { Invoke(new Action(() => { textBox1.Text = "Write Data Success"; })); } else { string message = "Write Data Failed 0x" + nRet.ToString("X8"); // Show full hex error format Invoke(new Action(() => { textBox1.Text = message; })); } }); } static String buffer = String.Empty; private void btnRead_Click(object sender, EventArgs e) { // Capture necessary UI state on the UI thread before spinning off byte sectorAddr = (byte)comboBox3.SelectedIndex; byte blockAddr = (byte)comboBox4.SelectedIndex; byte keyType = 0x30; // 0x30 for Key A, 0x31 for Key B[cite: 1] byte[] keyData = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // Spin off a separate background thread using a lambda expression Task.Run(() => { int nRet; byte[] readData = new byte[20]; // Allocate a fixed character array buffer for RecordInfo char[] recordInfo = new char[256]; // 1. Detect the S50 card[cite: 1] nRet = K720.K720_S50DetectCard(K720.Instance.ComHandle, K720.Instance.MacAddr, recordInfo); if (nRet != 0) { Invoke(new Action(() => { textBox1.Text = "Card Detection Failed 0x" + nRet.ToString("X2"); })); return; } // 2. Load and verify the sector security key[cite: 1] nRet = K720.K720_S50LoadSecKey(K720.Instance.ComHandle, K720.Instance.MacAddr, sectorAddr, keyType, keyData, recordInfo); if (nRet != 0) { Invoke(new Action(() => { textBox1.Text = "Key Authentication Failed 0x" + nRet.ToString("X2"); })); return; } // 3. Read the target block data[cite: 1] nRet = K720.K720_S50ReadBlock(K720.Instance.ComHandle, K720.Instance.MacAddr, sectorAddr, blockAddr, readData, recordInfo); if (nRet == 0) { string str = ""; for (int i = 0; i < 16; i++) { str += readData[i].ToString("X2"); // Formats each byte as uppercase Hex[cite: 1] } // Safely update UI controls from the background thread Invoke(new Action(() => { textBox3.Text = str; textBox1.Text = "Read Data Success"; })); } else { string message = "Read Data Failed 0x" + nRet.ToString("X2"); Invoke(new Action(() => { textBox1.Text = message; })); } }); } private void textBox18_TextChanged(object sender, EventArgs e) { TextBox textBox = sender as TextBox; if (textBox != null) { buffer = textBox.Text; Console.WriteLine(buffer); } } private void comboBox6_SelectedIndexChanged(object sender, EventArgs e) { ComboBox comboBox = sender as ComboBox; if (comboBox != null && comboBox.SelectedItem != null) { string selectedItem = comboBox.SelectedItem.ToString(); command = selectedItem; } } private void btnDispense_Click(object sender, EventArgs e) { // 1. Simulate a payload matching what the API would receive var fakePayload = new CardTaskResponse { Command = "Dispense" }; // 2. Call the identical central hardware router dynamic result = _manager.ExecuteHardwareCommand(fakePayload); // 3. Update UI based on the response layout if (result.status == "success") { textBox1.Text = $"Dispensed! Device code: {result.hardwareResponse}"; } else { MessageBox.Show($"Error: {result.message}"); } } private void btnClearLog(object sender, EventArgs e) { Utils.ClearLog(); rtbLogs.Text = ""; } } }