Bài viết hôm nay tiếp tục hướng dẫn các cách tải dữ liệu chấm công từ máy chấm công trong lập trình C# Windows Form.
[C#] Phần mềm tải dữ liệu máy chấm công
Trong bài viết này, chương trình demo của đang kết nối với máy chấm công Ronald Jack, một thương hiệu máy chấm công phổ biến trên thị trường.
Máy chấm công này có cung cấp cho chúng ta thư viện Interop.zkemkeeper để làm việc với máy chấm công.
Với thư viện, này chúng ta có thể làm nhiều việc trên máy chấm công: thêm nhân viên mới, xóa nhân viên, khóa nhân viên…
Tuy nhiên trong bài viết này, chỉ demo cách tải dữ liệu chấm công trên máy về GridView trên Winform.
Dưới đây là giao diện demo ứng dụng tools tải dữ liệu chấm công:
Phần mềm này, tích hợp chạy đa luồng, nên thời gian tải dữ liệu trên nhiều máy sẽ rất nhanh, như hình demo các bạn thấy.
Thông số port mặc định của máy thường là: 4370, nếu máy của bạn cấu hình trên port khác, các bạn có thể thay đổi port ở code bên dưới.
Đầu tiên các bạn tạo một class AttLogHelper.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using zkemkeeper; namespace AttLog { public static class AttLogHelper { public const int TCP_PORT = 4370; private const int CONNECT_TIMEOUT = 4000; private const int DATA_FILE_FLAG = 1; // Log data file private const int RECORD_COUNT_FLAG = 6; // Log data file private static int iMachineNumber = 1;//In fact,when you are using the tcp/ip communication,this parameter will be ignored,that is any integer will all right.Here we use 1. private static readonly Random m_random = new Random(); private static readonly string HOB_TEMP_CHAMCONG_DIR = Path.GetTempPath() + "hob_chamcong\\"; //1 dòng dữ liệu LOG máy chấm công cố định là 16 byte private const int ROW_LOG_BYTE_LENGTH = 16; //1 dòng dữ liệu USER máy chấm công cố định là 28 byte private const int ROW_USER_BYTE_LENGTH = 28; private const string CHAMCONG_LOG_FILENAME = "extlog.dat"; private const string CHAMCONG_USER_FILENAME = "user.dat"; //file lưu mã nhân viên - seri thẻ private const long MAX_SERI = 4294967295; //return false nghĩa là dữ liệu máy chấm công có lỗi cấu trúc public static bool ParseLog2Datatable(ref byte[] buff, DataTable outDt) { int manv = 0; DateTime ngaygio = default(DateTime); int startIndex = 0; var Today = DateTime.Now; DataRow row = default(DataRow); outDt.Clear(); outDt.Columns.Add("manv", typeof(int)); outDt.Columns.Add("ngaygio", typeof(DateTime)); //1 dòng dữ liệu LOG máy chấm công cố định là 16 byte //buff.Length > 0 AndAlso if (buff.Length % ROW_LOG_BYTE_LENGTH == 0) { for (var r = 0; r <= (buff.Length / ROW_LOG_BYTE_LENGTH) - 1; r++) { startIndex = ROW_LOG_BYTE_LENGTH * r; manv = ReadManvFromByteArr(buff, startIndex); ngaygio = ReadDateFromByteArr(buff, startIndex + 4); if (!validDateLog(ngaygio, Today)) { //lỗi cấu trúc file log, phải chuyển sang dùng zkem thay thế return false; } row = outDt.NewRow(); row["manv"] = manv; row["ngaygio"] = ngaygio; outDt.Rows.Add(row); } return true; } return false; } //return false nghĩa là dữ liệu máy chấm công có lỗi cấu trúc public static bool ParseUser2Datatable(ref byte[] buff, DataTable outDt) { int startIndex = 0; DataRow row = default(DataRow); outDt.Clear(); outDt.Columns.Add("manv", typeof(int)); outDt.Columns.Add("masothe", typeof(long)); outDt.Columns.Add("khoathe", typeof(bool)); //1 dòng dữ liệu USER máy chấm công cố định là 28 byte //buff.Length > 0 AndAlso if (buff.Length % ROW_USER_BYTE_LENGTH == 0) { for (var r = 0; r <= (buff.Length / ROW_USER_BYTE_LENGTH) - 1; r++) { startIndex = ROW_USER_BYTE_LENGTH * r; row = outDt.NewRow(); row["manv"] = ReadManvFromByteArr(buff, startIndex); row["masothe"] = ReadMasotheFromByteArr(buff, startIndex + 16); row["khoathe"] = buff[startIndex + 21] == 0; outDt.Rows.Add(row); } return true; } return false; } //Chuyển ngược datatable USER thành mảng Byte public static byte[] ConvertTbl_UserToByteArr(DataTable dt) { byte[] buff = new byte[] { }; int r = 0; int startIndex = 0; byte b1 = 0; byte b2 = 0; int manv = 0; long masothe = 0; bool khoathe = false; if (dt.Rows.Count > 0) { buff = new byte[dt.Rows.Count * ROW_USER_BYTE_LENGTH - 1 + 1]; foreach (DataRow row in dt.Rows) { startIndex = r * ROW_USER_BYTE_LENGTH; r++; manv = Convert.ToInt32(row["manv"]); b1 = Convert.ToByte(manv >> 0 & 0xFF); b2 = Convert.ToByte(manv >> 8 & 0xFF); buff[startIndex] = b1; buff[startIndex + 1] = b2; buff[startIndex + 24] = b1; buff[startIndex + 25] = b2; masothe = Convert.ToInt64(row["masothe"]); buff[startIndex + 16] = Convert.ToByte(masothe >> 0 & 0xFF); buff[startIndex + 17] = Convert.ToByte(masothe >> 8 & 0xFF); buff[startIndex + 18] = Convert.ToByte(masothe >> 16 & 0xFF); buff[startIndex + 19] = Convert.ToByte(masothe >> 24 & 0xFF); khoathe = Convert.ToBoolean(row["khoathe"]); buff[startIndex + 21] = Convert.ToByte(khoathe); } } return buff; } public static int getRecordCount(zkemkeeper.CZKEM axCZKEM1) { int result = -1; if (axCZKEM1 == null) { return result; } axCZKEM1.EnableDevice(iMachineNumber, false);//disable the device if (!axCZKEM1.GetDeviceStatus(iMachineNumber, RECORD_COUNT_FLAG, ref result)) { result = -1; } axCZKEM1.EnableDevice(iMachineNumber, true);//enable the device return result; } private static string genRandomFilename() { DateTime now = DateTime.Now; DateTime sd = new DateTime(1970, 1, 1); string filename = (int)now.Subtract(sd).TotalDays + "." + (int)now.TimeOfDay.TotalMilliseconds + "." + m_random.Next(10000, 99999) + ".data.dat"; return filename; } public static byte[] downloadDataFile(zkemkeeper.CZKEM axCZKEM1) { if (axCZKEM1 == null) { return null; } string filename = HOB_TEMP_CHAMCONG_DIR + genRandomFilename(); if (!Directory.Exists(HOB_TEMP_CHAMCONG_DIR)) { try { Directory.CreateDirectory(HOB_TEMP_CHAMCONG_DIR); } catch (System.Exception) { return null; } } bool isSuccess = axCZKEM1.GetDataFile(iMachineNumber, DATA_FILE_FLAG, filename); if (isSuccess) { byte[] bt = null; try { bt = File.ReadAllBytes(filename); File.Delete(filename); } catch (Exception) { } return bt; } else { return null; } } private static bool validDateLog(DateTime testDate, DateTime today) { return today.Date >= testDate.Date && testDate > today.AddDays(-35); } private static int ReadManvFromByteArr(byte[] buff, int startIndex) { return buff[startIndex] + buff[startIndex + 1] * 256; } public static async Task<CZKEM> Connect(string ip) { zkemkeeper.CZKEM axCZKEM1 = new zkemkeeper.CZKEM(); await Task.Factory.StartNew(new Action(() => { axCZKEM1.Connect_Net(ip, TCP_PORT); })); return axCZKEM1; } private static DateTime ReadDateFromByteArr(byte[] buff, int startIndex) { long dateInt = 0; dateInt = System.Convert.ToInt64(buff[startIndex] + buff[startIndex + 1] * 256 + buff[startIndex + 2] * 65536 + buff[startIndex + 3] * Convert.ToInt64(16777216)); int Year = 0; int Month = 0; int Day = 0; int Hour = 0; int Minute = 0; int Second = 0; Second = (int)(dateInt % 60); dateInt /= 60; Minute = (int)(dateInt % 60); dateInt /= 60; Hour = (int)(dateInt % 24); dateInt /= 24; Day = (int)(dateInt % 31 + 1); dateInt /= 31; Month = (int)(dateInt % 12 + 1); dateInt /= 12; Year = System.Convert.ToInt32(dateInt + 2000); return new DateTime(Year, Month, Day, Hour, Minute, Second); } private static long ReadMasotheFromByteArr(byte[] buff, int startIndex) { long Masothe = System.Convert.ToInt64(buff[startIndex] + buff[startIndex + 1] * 256 + buff[startIndex + 2] * 65536 + buff[startIndex + 3] * Convert.ToInt64(16777216)); return Masothe; } private static string ReadUsernameFRomByteArr(byte[] buff, int startIndex) { if (buff[startIndex] == 0) { return ""; } else { var nameLen = 1; for (var r = 1; r <= 7; r++) { if (buff[startIndex + r] == 0) { break; } else { nameLen++; } } return Encoding.Default.GetString(buff, startIndex, nameLen); } } } } |
Và source code cho Form Main:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
using DevExpress.XtraEditors; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using zkemkeeper; namespace AttLog { public partial class Form1 : DevExpress.XtraEditors.XtraForm { private int _soMayChon; DataTable dataTableResult; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { var dataTable = new DataTable(); dataTable.Columns.Add("ip"); dataTable.Columns.Add("tinhtrang"); dataTable.Columns.Add("noidung"); dataTable.Rows.Add("172.16.0.91"); dataTable.Rows.Add("172.16.0.92"); dataTable.Rows.Add("172.16.0.93"); dataTable.Rows.Add("172.16.0.94"); gridControl1.DataSource = dataTable; dataTableResult = new DataTable(); dataTableResult.Columns.Add("manv"); dataTableResult.Columns.Add("ngaygio"); gridControl2.DataSource = dataTableResult; } private void ZKEM_download_chamcong(IProgress<int> progress) { gridView1.CloseEditor(); gridView1.PostEditor(); var selectedRows = gridView1.GetSelectedRows(); _soMayChon = selectedRows.Count(); int countFinish = 0; BeginInvoke(new Action(() => { btn_DownloadAtt.Enabled = false; foreach (var r in selectedRows) { gridView1.SetRowCellValue(r, "tinhtrang", ""); gridView1.SetRowCellValue(r, "noidung", ""); } })); Parallel.ForEach(selectedRows, async rowHandle => { string deviceIp = Convert.ToString(gridView1.GetDataRow(rowHandle)["ip"]); TinhTrangKetNoi(rowHandle, "Đang kết nối ZKEM..."); CZKEM izkem = await AttLogHelper.Connect(deviceIp); if (izkem != null) { int recordCount = AttLogHelper.getRecordCount(izkem); if (recordCount == 0) { TinhTrangKetNoi(rowHandle, "Máy không có dữ liệu"); } else { TinhTrangKetNoi(rowHandle, "Đã kết nối. Đang tải..."); byte[] data = AttLogHelper.downloadDataFile(izkem); izkem.Disconnect(); if (data != null) { var dt = new DataTable(); if (AttLogHelper.ParseLog2Datatable(ref data, dt)) { gridControl2.BeginInvoke(new Action(() => { foreach(DataRow dr in dt.Rows) { var workRow = dataTableResult.NewRow(); workRow[0] = dr["manv"]; workRow[1] = dr["ngaygio"]; dataTableResult.Rows.Add(workRow); } })); NoidungKetNoi(rowHandle, "Tìm thấy " + dt.Rows.Count + " dòng"); } } else { TinhTrangKetNoi(rowHandle, "Tải file chấm công thất bại"); } } } else { TinhTrangKetNoi(rowHandle, "Lỗi kết nối. Thử lại sau."); } Interlocked.Increment(ref countFinish); var percent = Convert.ToInt32(Convert.ToDouble(countFinish) / _soMayChon * 100d); progress.Report(percent); }); } private void TinhTrangKetNoi(int rowHandle, string msg) { gridControl1.BeginInvoke((Action)(() => { gridView1.SetRowCellValue(rowHandle, "tinhtrang", msg); })); } private void NoidungKetNoi(int rowHandle, string msg) { gridControl1.BeginInvoke((Action)(() => { gridView1.SetRowCellValue(rowHandle, "noidung", msg); })); } private async void btnDownload_Click(object sender, EventArgs e) { if (gridView1.SelectedRowsCount > 0) { progressBarControl1.EditValue = 0; var progress = new Progress<int>(percent => { BeginInvoke(new Action(() => { progressBarControl1.EditValue = percent; if(percent == 100) { btn_DownloadAtt.Enabled = true; } })); }); await Task.Run(() => ZKEM_download_chamcong(progress)); } else { XtraMessageBox.Show("Bạn vui lòng chọn các máy chấm công để tải dữ liệu kết nối."); } } } } |
Cám ơn các bạn đã đọc!
Nguồn: http://laptrinhvb.net