Hướng dẫn tạo Windows Desktop App C# kết nối PLC Keyence KV Series – Đọc Device DM100

Giới thiệu

Bài viết này hướng dẫn cách tạo ứng dụng Windows Desktop bằng C# Windows Forms để kết nối với PLC Keyence KV Series qua giao thức MC Protocol và đọc trạng thái device DM100.

Yêu cầu

  • Visual Studio 2019/2022 (Community Edition miễn phí)
  • .NET Framework 4.7.2 hoặc .NET 6/7/8
  • PLC Keyence KV Series với Ethernet
  • Kết nối mạng TCP/IP

Tạo Project trong Visual Studio

  1. File → New → Project → Windows Forms App (.NET Framework)
  2. Đặt tên: PLCKeyenceMonitor
  3. .NET Framework 4.7.2
  4. Click Create

Code C# đầy đủ

File: Form1.cs

using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace PLCKeyenceMonitor
{
    public partial class Form1 : Form
    {
        private TcpClient tcpClient;
        private NetworkStream networkStream;
        private Thread monitorThread;
        private bool isMonitoring = false;
        private bool isConnected = false;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            txtIP.Text = "192.168.0.1";
            txtPort.Text = "8501";
            txtDevice.Text = "DM100";
            UpdateStatus("Chưa kết nối");
        }

        // Xây dựng MC Protocol 3E Frame
        private byte[] BuildMCProtocolFrame(string device, bool isWrite = false, ushort value = 0)
        {
            byte devCode;
            int devAddr;
            
            if (device.StartsWith("DM"))
            {
                devCode = 0xA8;
                devAddr = int.Parse(device.Substring(2));
            }
            else if (device.StartsWith("R"))
            {
                devCode = 0x9C;
                devAddr = int.Parse(device.Substring(1));
            }
            else if (device.StartsWith("T"))
            {
                devCode = 0xC2;
                devAddr = int.Parse(device.Substring(1));
            }
            else
            {
                devCode = 0xA8;
                devAddr = int.Parse(device);
            }

            using (var ms = new System.IO.MemoryStream())
            {
                ms.WriteByte(0x50);
                ms.WriteByte(0x00);
                ms.WriteByte(0x00);
                ms.WriteByte(0xFF);
                ms.WriteByte(0xFF);
                ms.WriteByte(0x03);
                ms.WriteByte(0x00);
                
                long dataLengthPos = ms.Position;
                ms.WriteByte(0x00);
                ms.WriteByte(0x00);
                
                ms.WriteByte(0x00);
                ms.WriteByte(0x10);
                
                if (isWrite)
                {
                    ms.WriteByte(0x01);
                    ms.WriteByte(0x14);
                }
                else
                {
                    ms.WriteByte(0x01);
                    ms.WriteByte(0x04);
                }
                
                ms.WriteByte(0x00);
                ms.WriteByte(0x00);
                ms.WriteByte(devCode);
                
                ms.WriteByte((byte)(devAddr & 0xFF));
                ms.WriteByte((byte)((devAddr >> 8) & 0xFF));
                ms.WriteByte((byte)((devAddr >> 16) & 0xFF));
                
                ms.WriteByte(0x01);
                ms.WriteByte(0x00);
                
                if (isWrite)
                {
                    ms.WriteByte((byte)(value & 0xFF));
                    ms.WriteByte((byte)((value >> 8) & 0xFF));
                }
                
                long currentPos = ms.Position;
                int dataLength = (int)(currentPos - dataLengthPos - 2);
                ms.Position = dataLengthPos;
                ms.WriteByte((byte)(dataLength & 0xFF));
                ms.WriteByte((byte)((dataLength >> 8) & 0xFF));
                
                return ms.ToArray();
            }
        }

        private (ushort? value, string message) ParseResponse(byte[] data)
        {
            if (data.Length < 11)
                return (null, "Response too short");
            
            ushort endCode = (ushort)(data[9] | (data[10] <= 13)
            {
                ushort value = (ushort)(data[11] | (data[12] < 0)
                {
                    Array.Resize(ref buffer, bytesRead);
                    var (value, msg) = ParseResponse(buffer);
                    
                    if (value.HasValue)
                        Log($"📊 {device} = {value.Value} (0x{value.Value:X4})");
                    else
                        Log($"⚠️ Lỗi: {msg}");
                }
            }
            catch (Exception ex)
            {
                Log($"❌ Lỗi đọc: {ex.Message}");
            }
        }

        private void btnMonitor_Click(object sender, EventArgs e)
        {
            if (isMonitoring)
            {
                isMonitoring = false;
                btnMonitor.Text = "Monitor";
                Log("⏹️ Đã dừng monitor");
            }
            else
            {
                isMonitoring = true;
                btnMonitor.Text = "Dừng";
                Log("▶️ Bắt đầu monitor...");
                monitorThread = new Thread(MonitorLoop);
                monitorThread.IsBackground = true;
                monitorThread.Start();
            }
        }

        private void MonitorLoop()
        {
            while (isMonitoring && isConnected)
            {
                Invoke(new Action(() => ReadDevice()));
                Thread.Sleep(1000);
            }
        }

        private void UpdateStatus(string message)
        {
            lblStatus.Text = $"Trạng thái: {message}";
        }

        private void Log(string message)
        {
            string timestamp = DateTime.Now.ToString("HH:mm:ss");
            txtLog.AppendText($"[{timestamp}] {message}\r\n");
        }
    }
}

Thiết kế Form

  • GroupBox “Cấu hình kết nối”: Label + TextBox (txtIP, txtPort)
  • GroupBox “Device”: Label + TextBox (txtDevice)
  • Button: btnConnect, btnRead, btnMonitor
  • TextBox: txtLog (MultiLine, ScrollBars=Vertical)
  • StatusStrip: lblStatus

MC Protocol Frame Structure

Frame format tương tự Python: 50 00 (Subheader) + Network/PC/Module + Data Length + Monitoring Timer + Command + Device Code + Address

Chạy ứng dụng

  1. Nhấn F5 hoặc Start để chạy
  2. Nhập IP PLC (ví dụ: 192.168.0.1)
  3. Nhấn “Kết nối”
  4. Nhập device (DM100)
  5. Nhấn “Đọc” hoặc “Monitor”

Device Codes

DM: 0xA8 | R: 0x9C | MR: 0x90 | T: 0xC2 | C: 0xC3

Mở rộng

  • Đọc nhiều device (RDS command)
  • Ghi device (WR command)
  • Kết nối Modbus TCP
  • Chart real-time với LiveCharts
  • Export CSV/Excel

Code dựa trên tài liệu Keyence KV Series. Tested với KV-7500 và KV-8000.

Chuyên mục: Blog
Bài viết đã được tạo 7

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Bài liên quan

Bắt đầu nhập từ khoá bên trên và nhấp enter để tìm kiếm. Nhấn ESC để huỷ.

Trở lên trên