// LEARN · MC 协议入门

用 C# 对比:直接写 MC 协议、McpX 与 MX Component

将「读取三菱电机 PLC 的 D100〜D102」这一相同处理用 3 种方法编写并对比:自行组装 MC 协议报文、.NET 库 McpX、三菱电机的 MX Component,比较代码量、准备工作、支持环境与适用场景。

更新

用 C# 读写三菱电机 PLC 的方法大致有 3 种:自行组装 MC 协议报文、使用 McpX 这样的库,以及使用三菱电机的 MX Component。

本文用这 3 种方法编写同一个处理:「从通过 Ethernet 连接的 PLC 读取 D100〜D102 共 3 个字」。并排来看,就能清楚看到各自替你承担了什么、又把什么留给了你。

1. 直接编写 MC 协议

只使用 .NET 标准的 TcpClient,把3E 帧一文中读过的报文直接写成字节序列。D 的软元件代码为 A8,点数为 3。

using System.Buffers.Binary;
using System.Net.Sockets;

using var client = new TcpClient();
client.Connect("192.168.3.39", 5000);
using var stream = client.GetStream();

// 3E 帧・二进制:从 D100 批量读取 3 个字(0401)
byte[] request =
[
    0x50, 0x00,                   // 副帧头
    0x00, 0xFF, 0xFF, 0x03, 0x00, // 目标(本站)
    0x0C, 0x00,                   // 请求数据长度 = 12
    0x10, 0x00,                   // 监视定时器 = 4 秒
    0x01, 0x04, 0x00, 0x00,       // 命令 0401 / 子命令 0000
    0x64, 0x00, 0x00,             // 起始软元件编号 = 100
    0xA8,                         // 软元件代码 = D
    0x03, 0x00,                   // 点数 = 3
];
stream.Write(request);

// 读取响应的前 9 个字节(副帧头〜响应数据长度)
byte[] header = new byte[9];
stream.ReadExactly(header);
if (header[0] != 0xD0 || header[1] != 0x00)
    throw new InvalidDataException("不是 3E 帧的响应");
int length = BinaryPrimitives.ReadUInt16LittleEndian(header.AsSpan(7));

// 读取剩余部分(结束代码+数据)
byte[] body = new byte[length];
stream.ReadExactly(body);
ushort endCode = BinaryPrimitives.ReadUInt16LittleEndian(body);
if (endCode != 0)
    throw new Exception($"PLC 返回了错误: {endCode:X4}");

short[] values = new short[3];
for (int i = 0; i < values.Length; i++)
    values[i] = BinaryPrimitives.ReadInt16LittleEndian(body.AsSpan(2 + i * 2));

它确实能工作,但只覆盖了「以二进制读取 D 的 3 个字」这一种情况。真正要用时,还需要自己补上下面这些处理:

  • 读取 961 字以上时拆分请求
  • 组装 int、float 这类由 2 个字构成一个值的数据
  • 处理 M 这样的位软元件,以及 X、Y 这样十六进制编号的软元件
  • 没有响应时的超时处理,以及之后重新建立连接
  • 支持 iQ-R 用的子命令和远程密码

这是理解报文内容的最好方法。但若要集成到应用程序中,以上这些都得自己编写并测试。

2. 使用 McpX

通过 NuGet 添加 McpX,编写同样的处理。

using McpXLib;
using McpXLib.Enums;

using var mcpx = new McpX("192.168.3.39", 5000);
short[] values = mcpx.BatchRead<short>(Prefix.D, "100", 3);

第 1 种方法中组装报文与解析响应的工作,由 BatchRead<short> 这一行承担。把类型换成 int 或 float,字数的计算与值的组装也由 McpX 完成。超过 960 字的读取会自动拆分发送。

PLC 返回错误时会抛出 McProtocolException,可以通过 ErrorCode 属性取得错误代码。

McpX 完全用 .NET 编写,因此除 Windows 外,也能在 Linux・macOS 上运行。相应地,它支持的通信路径是 Ethernet(TCP/UDP)与 GX Simulator3,不能通过 USB 或串口连接。

3. 使用 MX Component

MX Component 是三菱电机提供的通信库。这里按手册的步骤使用 Version 5 的 64 位版。需要事先准备以下 2 项:

  1. 安装 MX Component,用附带的通信设置实用程序设置连接目标。每个设置会分配一个称为逻辑站号的编号
  2. 在 Visual Studio 的[添加引用]中,把 COM 的「ActUtlType64 Control」添加到项目
using ActUtlType64Lib;

var plc = new ActUtlType64Class();
plc.ActLogicalStationNumber = 1; // 在通信设置实用程序中创建的逻辑站号

int ret = plc.Open();
if (ret != 0)
    throw new Exception($"连接失败: {ret:X8}");
try
{
    short[] values = new short[3];
    ret = plc.ReadDeviceBlock2("D100", values.Length, out values[0]);
    if (ret != 0)
        throw new Exception($"读取失败: {ret:X8}");
}
finally
{
    plc.Close();
}

代码中不出现 IP 地址和端口号。连接目标的信息保存在通信设置实用程序一侧,代码只用逻辑站号来指定它。

ReadDeviceBlock2 以 "D100" 这样的字符串接收软元件名,以 2 字节(short)为单位读取值。读取目标数组需由调用方分配,大小不小于点数。手册提醒,区域不足时可能发生应用程序错误等严重现象。错误以返回值而非异常的形式返回,因此每次调用后都要确认是否为 0。

MX Component 的优势在于通信路径多。除 Ethernet 外,还能以相同的写法通过以下路径连接:

  • USB、串口
  • MELSECNET/H、CC-Link IE 等网络板卡
  • GX Simulator2/3 等仿真器

另一方面,它只能在 Windows 上运行,并且需要购买产品。

三种方法并排比较

直接编写 MC 协议McpXMX Component
需要添加的东西无(.NET 标准)NuGet 包 McpX安装 MX Component 并添加 COM 引用
连接目标的指定代码中写 IP 与端口构造函数中写 IP 与端口通信设置实用程序+逻辑站号
运行的 OS能运行 .NET 的环境Windows・Linux・macOSWindows
通信路径自己实现的部分(Ethernet)Ethernet(TCP/UDP)、GX Simulator3Ethernet、USB、串口、各种网络板卡、仿真器等
值的类型自己从字节转换按类型指定 bool・short・int・float 等ReadDeviceBlock2 为 short,int 等需自己组装
错误处理自己判断结束代码异常(McProtocolException)每次确认返回值
一次可指定的点数超过上限(批量读取为 960 字)时自己拆分超过上限时自动拆分在软元件范围内可任意指定点数
许可—MIT(免费・可商用)收费

如何选择

选择时要考虑以下 3 个问题。从上往下依次回答,通常就能确定。

1. 是否通过 Ethernet 以外的路径连接

若通过 USB、串口,或 MELSECNET/H、CC-Link IE 等网络板卡连接 PLC,应选择 MX Component。McpX 和直接编写都只能通过 Ethernet 通信。

2. 是否在 Windows 以外运行

若通过 Ethernet 连接,接下来考虑运行环境。在 Linux 服务器、Docker 容器、Raspberry Pi 或 macOS 开发机上运行,应选择 McpX。MX Component 只能在 Windows 上运行。

3. 如何考虑分发与许可

在 Windows 上通过 Ethernet 连接时,McpX 和 MX Component 都能实现。此时的决定因素是应用程序如何分发。

  • 适合 McpX 的情况:分发到大量电脑的应用,或提供给公司外部的应用。MIT 许可、免费,并且只需作为 NuGet 引用包含在应用中,无需在每台运行的电脑上安装或进行连接设置
  • 适合 MX Component 的情况:已经用 MX Component 管理连接目标、在公司内固定电脑上使用的应用。无需修改代码,就能在通信设置实用程序中更改连接目标

常见场景

场景选择
在 Linux 服务器上收集工厂运行数据,并显示在 Web 看板上McpX
在 Docker 中运行把 PLC 数据发送到云端的网关McpX
从检测设备的 Windows 应用操作通过 USB 连接的 PLCMX Component
在插有 CC-Link IE 板卡的电脑上统一监视多台 PLCMX Component
从 C# 以外的语言或单片机与 PLC 通信直接编写(本入门的报文知识)

无论选择哪种,报文知识都有用

实际系统很少从报文开始自行编写。但只要了解报文结构,即使使用库也能追查故障原因:用 Wireshark 查看通信时,能看懂每个字节的含义,也能明白 PLC 返回的错误代码指的是什么。3E 帧与错误代码两篇文章提供的正是这些知识。

出处

  • MX Component Version 5 参考手册(日文版,SH-082394)p.29, 48〜49, 56〜57, 473〜478, 647
  • MELSEC 通信协议参考手册(日文版,SH-080003)p.41〜44, 附录 7 p.473
  • SLMP 参考手册(日文版,SH-080931)p.34〜38