NEWS

[C#] Hướng dẫn download dữ liệu máy chấm công

[C#] Hướng dẫn download dữ liệu máy chấm công
Đăng bởi: Thảo Meo - Lượt xem: 14763 08:59:31, 14/10/2020PHẦN MỀM

Xin chào các bạn, bài viết hôm nay mình sẻ tiếp tục hướng dẫn các bạn cách tải dữ liệu chấm công từ máy chấm công trong lập trình C# winform.

[C#] Phần mềm tải dữ liệu máy chấm công

Ronald-jack-t8_3ucr-kg

Trong bài viết này, chương trình demo của mình đ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, mình 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:

downloadATT

Phần mềm này, mình 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 cho mình một class  AttLogHelper.cs 

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:

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.");
            }
        }
    }
}

Thanks for watching!

DOWNLOAD SOURCE

THÔNG TIN TÁC GIẢ

BÀI VIẾT LIÊN QUAN

[C#] Hướng dẫn download dữ liệu máy chấm công
Đăng bởi: Thảo Meo - Lượt xem: 14763 08:59:31, 14/10/2020PHẦN MỀM