Hướng dẫn tích hợp máy quét mã vạch Newland với ứng dụng C# WinForms qua giao tiếp nối tiếp

Tổng quan về kết nối phần cứng

Trong môi trường sản xuất và quản lý kho bãi, việc thu thập dữ liệu từ máy quét mã vạch thường được thực hiện thông qua giao thức RS-232 hoặc giao tiếp giả lập cổng COM. Trước khi triển khai mã nguồn, cần xác minh trình điều khiển thiết bị đã hoạt động ổn định. Trường hợp hệ điều hành không nhận diện được cổng COM ảo sau khi kết nối USB, người vận hành cần quét mã cấu hình Virtual COM Mode trong tài liệu kỹ thuật của thiết bị để kích thích trình điều khiển ánh xạ dữ liệu vào luồng nối tiếp.

Xây dựng lớp quản lý luồng dữ liệu

Để đảm bảo tính ổn định và tái sử dụng, lớp xử lý cổng COM cần được đóng gói riêng biệt, tách rời khỏi logic giao diện. Lớp này sẽ quản lý vòng đời kết nối, xử lý sự kiện đọc dữ liệu bất đồng bộ và giải mã mảng byte thành chuỗi ký tự theo chuẩn ASCII.

using System;
using System.IO.Ports;
using System.Text;

namespace ScannerIntegration
{
    public class BarcodeComHandler
    {
        private readonly SerialPort _comDevice;
        public event Action<string> DataCaptured;

        public BarcodeComHandler()
        {
            _comDevice = new SerialPort();
            _comDevice.DataReceived += HandleIncomingStream;
        }

        public bool EstablishConnection(string portName, int baudRate = 9600, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One)
        {
            try
            {
                if (_comDevice.IsOpen)
                    _comDevice.Close();

                _comDevice.PortName = portName;
                _comDevice.BaudRate = baudRate;
                _comDevice.DataBits = dataBits;
                _comDevice.Parity = parity;
                _comDevice.StopBits = stopBits;
                _comDevice.ReadTimeout = 1000;
                _comDevice.WriteTimeout = 1000;

                _comDevice.Open();
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"Lỗi khởi tạo kết nối: {ex.Message}");
                return false;
            }
        }

        private void HandleIncomingStream(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                int bytesAvailable = _comDevice.BytesToRead;
                if (bytesAvailable <= 0) return;

                byte[] buffer = new byte[bytesAvailable];
                int bytesRead = _comDevice.Read(buffer, 0, bytesAvailable);

                if (bytesRead > 0)
                {
                    string decodedText = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                    DataCaptured?.Invoke(decodedText.Trim());
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"Lỗi đọc luồng dữ liệu: {ex.Message}");
            }
        }

        public bool Disconnect()
        {
            try
            {
                if (_comDevice.IsOpen)
                {
                    _comDevice.Close();
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        public bool IsConnected => _comDevice.IsOpen;
    }
}

Triển khai giao diện người dùng WinForms

Giao diện chính cần xử lý an toàn luồng (thread-safe) khi nhận sự kiện từ chuỗi COM. Dưới đây là cách tổ chức form chính với cơ chế Invoke để cập nhật nhật ký quét thời gian thực và điều khiển trạng thái các nút tương tác.

using System;
using System.Configuration;
using System.IO.Ports;
using System.Windows.Forms;

namespace ScannerIntegration
{
    public partial class MainScannerForm : Form
    {
        private readonly BarcodeComHandler _scannerHandler;

        public MainScannerForm()
        {
            InitializeComponent();
            _scannerHandler = new BarcodeComHandler();
            _scannerHandler.DataCaptured += ProcessScannedData;
        }

        private void MainScannerForm_Load(object sender, EventArgs e)
        {
            btnDisconnect.Enabled = false;
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            string targetPort = ConfigurationManager.AppSettings["SelectedComPort"] ?? "COM1";
            bool success = _scannerHandler.EstablishConnection(targetPort, 9600, 8, Parity.None, StopBits.One);

            if (success)
            {
                UpdateStatus("Thiết lập kết nối thành công tới " + targetPort, true);
            }
            else
            {
                MessageBox.Show("Không thể mở cổng COM. Vui lòng kiểm tra trình điều khiển hoặc xung đột thiết bị.", "Lỗi kết nối", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            if (_scannerHandler.Disconnect())
            {
                UpdateStatus("Đã đóng luồng kết nối.", false);
            }
        }

        private void ProcessScannedData(string rawData)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<string>(ProcessScannedData), rawData);
                return;
            }
            string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {rawData}{Environment.NewLine}";
            txtLog.AppendText(logEntry);
        }

        private void UpdateStatus(string message, bool isConnected)
        {
            MessageBox.Show(message, "Trạng thái hệ thống", MessageBoxButtons.OK, MessageBoxIcon.Information);
            btnConnect.Enabled = !isConnected;
            btnDisconnect.Enabled = isConnected;
        }

        private void btnConfig_Click(object sender, EventArgs e)
        {
            using (var configForm = new ComConfigForm())
            {
                configForm.ShowDialog();
            }
        }

        private void txtLog_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            txtLog.Clear();
        }
    }
}

Quản lý tham số cấu hình động

Thay vì mã cứng tên cổng, ứng dụng sẽ sử dụng tệp app.config để lưu trữ tham số. Form cấu hình tự động truy vấn các cổng nối tiếp hiện có trong hệ điều hành, cho phép người dùng lựa chọn linh hoạt và ghi đè giá trị mới vào tệp cấu hình mà không cần biên dịch lại.

using System.Configuration;
using System.IO.Ports;
using System.Windows.Forms;

namespace ScannerIntegration
{
    public partial class ComConfigForm : Form
    {
        public ComConfigForm()
        {
            InitializeComponent();
        }

        private void ComConfigForm_Load(object sender, EventArgs e)
        {
            string[] availablePorts = SerialPort.GetPortNames();
            cmbComPort.Items.AddRange(availablePorts);

            string savedPort = ConfigurationManager.AppSettings["SelectedComPort"];
            if (!string.IsNullOrEmpty(savedPort) && cmbComPort.Items.Contains(savedPort))
            {
                cmbComPort.SelectedItem = savedPort;
            }
            else if (cmbComPort.Items.Count > 0)
            {
                cmbComPort.SelectedIndex = 0;
            }
        }

        private void btnSaveConfig_Click(object sender, EventArgs e)
        {
            if (cmbComPort.SelectedItem == null)
            {
                MessageBox.Show("Vui lòng lựa chọn cổng COM khả dụng.", "Cảnh báo", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            try
            {
                var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                config.AppSettings.Settings["SelectedComPort"].Value = cmbComPort.SelectedItem.ToString();
                config.Save(ConfigurationSaveMode.Modified);
                ConfigurationManager.RefreshSection("appSettings");
                this.DialogResult = DialogResult.OK;
                this.Close();
            }
            catch (ConfigurationErrorsException ex)
            {
                MessageBox.Show($"Không thể cập nhật tệp cấu hình: {ex.Message}", "Lỗi hệ thống", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}

Thẻ: csharp WinForms serialport industrial-automation barcode-scanner

Đăng vào ngày 8 tháng 8 lúc 08:13