ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

三菱CNC数据采集实战:C#实现EZSocket通信与上位机Demo

2026/10/4 21:59:16 拓冰建站 浏览量
三菱CNC数据采集实战:C#实现EZSocket通信与上位机Demo 简介工业数据采集是智能制造与设备联网的前提。数控机床CNC的实时坐标、主轴转速、倍率与告警等数据通常通过专用网络协议如三菱EZSocket基于TCP/IP读取。使用C#对SDK进行DllImport封装可在上位机中快速实现设备状态监控、数据转发及MES对接适用于车间设备联网、加工中心实时监测等场景。一套三菱CNC数据采集的C#源码Demo覆盖EZSocket通信选型、报文组包、数值换算、断线自愈等关键环节帮助工程师快速打通设备到信息系统的数据通路。1. 三菱CNC数据采集Demo这套C#源码到底帮你打开了哪扇门给车间里的三菱加工中心做数据采集最容易卡住的不是C#写不好而是设备通信协议像一口黑匣子手册几百页地址映射藏在附录能直接跑通的示例代码又少。这套三菱CNC数据采集Demo附带C#源码就是把黑匣子打开一条缝——用网口通信库把坐标、主轴转速、进给倍率、报警信息读出来再在C#上位机里展示和转发。它适合正在做设备联网、MES对接的工程师也适合想搞懂CNC通信原理的上位机新手。看懂这个Demo改一改地址表就能接到自家机床上。2. 三菱CNC通信选型为什么走EZSocket而不是改PLC程序三菱CNC不是一个简单的PLC它自带完整的数控系统里面跑的坐标、转速、倍率、告警并不是都暴露在PLC寄存器里。想把这些数据拿出来常见有三条路改PLC程序、买协议网关、直接用CNC以太网库。改PLC要动设备厂家程序车间不敢让你动协议网关要花钱还要看机型而三菱官方提供了一套基于TCP/IP的以太网通信库业内常叫EZSocket接口机床侧只要在参数里打开以太网功能就能用C#直接读内部数据。这就是标题里那个Demo的路线也是目前做三菱CNC数据采集最贴合、最少外部依赖的方案。2.1 三条采集路线怎么选网口库、OPC、硬接线先说结论如果目标只是把一台三菱加工中心的数据搬出来优先走网口SDKEZSocket。如果你要同时接发那科、西门子、马扎克这些异构设备才考虑OPC UA或者MTConnect这类标准化协议但前提是机床侧得支持而且不少老机型不支持这种接口最后还是得回到各家私有协议。硬接线IO是最老的办法拉线到PMC的I/O点能拿到运行/停止、报警灯这种开关量但拿不到坐标和程序号而且布线工程大适合对实时性要求极高又不差钱的老车间。这个Demo走网口SDK核心原因有三个。一是三菱CNC把大部分内部数据都通过SDK开放了坐标、转速、倍率、程序号、告警都读得到二是不需要碰PLC梯形图程序对现场风险最小三是一台普通工控机加一个网口就能实现硬件成本低。代价也很明确协议是二进制帧格式文档是英文和日文地址表要按机床型号查附录第一次读写有一定学习成本。这也是为什么我说“会写C#不等于会做采集”真正的门槛在协议理解。2.2 EZSocket的报文与地址先读懂“读什么”再动手EZSocket的通信过程可以拆成三件事建立连接、发读取请求、解析响应。连接是普通的TCP也有UDP模式常见默认端口是6832具体以机床参数为准。连接建立后客户端要发一个同步包三菱侧会回一个确认包这个同步包在官方文档里叫SYNC很多人在重连时翻车就是因为漏了这一步。读取请求是二进制帧不是ASCII文本。帧结构大致分两块固定头部和请求数据区。头部里有子信道号、消息类型、帧长度这类字段请求数据区里放指令码读状态、读坐标、读参数等、起始地址、读取长度。响应帧同样分头部和数据区数据区就是你要的原始值。关键问题是这些字段的偏移量和校验算法在不同 CNC 型号、不同固件版本里不完全一致所以任何人给你的Demo里的组包代码都必须跟你手上的官方手册核对一遍再上产线。地址编码是第二个坑。三菱CNC对外暴露的数据点以“地址编号”组织类似PLC的寄存器地址但分类更细有状态字、轴坐标、主轴数据、程序/刀具数据等。手册附录里的数据一览表会列出每个编号代表什么、什么格式、单位是什么。我一般会让客户先去机床操作面板翻到“参数-网络设置”确认IP再找到手册附录的“数据地址表”把我需要的那几行用荧光笔标出来然后才去读代码。顺序反过来的新手多半会把相对坐标当成机械坐标采回来。2.3 最小采集清单坐标、转速、倍率、告警一张表说清给车间做Demo建议一开始不要贪多。先把下面这张表里的数据读通你的Demo就能横向展示“机床实时状态”了。数据类型典型格式换算与说明程序号数字编码显示当前运行的NC程序号注意编码方式按手册机械坐标X/Y/Z有符号整型常见最小指令单位为0.001mm除以1000得到mm主轴实际转速有符号整型单位rpm读取后可与面板核对进给倍率编码值通常不是百分比需要查编码对照表主轴倍率编码值同上编码区间按手册运行状态位/字状态区分自动/手动/报警按位掩码解析报警信息告警号与文本文本可能是日文编码见后面乱码一节这个清单覆盖了设备联网最关心的“在干什么、干多快、出没出事”。采集频率上坐标和倍率按200ms轮询足够告警建议单独线程提高频率或者用NOTICE回调。要注意的是不同类型点位对应的指令码可能不同比如读坐标和读状态不是同一个指令所以一次轮询里通常要发多个请求。Demo里的“读多字”函数就是把同一指令下连续地址一次读回来减少网络往返。3. 用C#封装三菱CNC通信库DllImport、报文组包与数值换算路线定下来第二步就是把三菱SDK里的C风格接口请进C#。三菱官方SDK一般提供Windows下的DLL和头文件C#不能直接include头文件要用DllImport做P/Invoke封装。这块封装做得稳不稳直接决定后面所有读取逻辑好不好写。我见过不少人把DLL调用、组包、解析全部堆在窗体按钮事件里最后动一下界面就卡死那不是协议难是封装结构没搭好。3.1 把三菱SDK的DLL请进C#DllImport声明与位数匹配先说位数匹配拿到动态库第一件事看它是32位还是64位。常见的老版本Ezsock.dll是32位你的C#进程就必须在x86模式下编译新版有的做了x64就按64位编译。位不匹配不会编译报错运行时调用直接进程崩溃这是第一道要踩的坑。下面是最小主义的DllImport声明用于连接和关闭using System; using System.Runtime.InteropServices; internal static class NativeCnc { // 以你手上官方SDK头文件里的导出名为准这里用常见命名占位 [DllImport(Ezsock.dll, EntryPoint EZ_Open, CallingConvention CallingConvention.StdCall)] internal static extern int Open( string host, // CNC的IP地址 int port, // 以太网端口常见6832 int timeoutMs, // 连接超时毫秒 ref byte subch, // 子信道号起始为0 byte[] clientId); // 客户端ID缓冲用来接收分配的ID [DllImport(Ezsock.dll, EntryPoint EZ_Close, CallingConvention CallingConvention.StdCall)] internal static extern int Close(byte subch); }逻辑说明Open的参数里subch是子信道号三菱协议里同一TCP连接可以带有多个子信道简单Demo用0号就行但必须传引用因为SDK会返回可用的信道号。clientId是SDK分配给你的客户端标识多机床连接时会用到。Close时把subch原样传回去否则服务端不认。参数说明连接超时一般设2到3秒比较合适太短在网线没插好时直接报错太长会让UI假死。端口不要想当然填6832先到CNC网络参数页面看一眼。另外不同SDK版本导出函数名有差异有的叫EZ_Open有的叫EZSOCK_OPEN还有的SDK封装成了mc_open这类名字拿到手先看一眼头文件或dumpbin导出表别照抄。提示如果程序一调用就进程崩溃且没有异常抛出优先怀疑位数不匹配这是P/Invoke最常见的事故源。3.2 组一个读多字的请求连接、发送、接收的最小闭环连接建好后读取流程是组请求帧 → 发送 → 接收响应帧 → 按帧头里的长度切出数据区。三菱的帧格式我在2.2说过字段偏移以手册为准这里给一个可改的骨架public byte[] BuildReadRequest(byte subch, int instruction, int startAddr, int count) { // 头部长度与字段布局必须对照你手上SDK的报文格式章节 int headerLen 30; var buf new byte[headerLen 8]; // 以下只是占位实际要把子信道、指令、起始地址、数量按手册布局写入 buf[0] subch; BitConverter.GetBytes(instruction).CopyTo(buf, 10); BitConverter.GetBytes(startAddr).CopyTo(buf, 14); BitConverter.GetBytes(count).CopyTo(buf, 18); return buf; }这块的注释不是偷懒是血的教训不同机床固件版本对头部字段的定义有差异直接抄网上代码翻车的概率很高。正确做法是拿官方头文件里的结构体定义或报文格式图一行行对字段偏移。发送和接收也一样Socket超时要单独设置。一般做法是给Socket设ReceiveTimeout为1到2秒读不到数据就主动清空缓冲区并重连。注意不要把Socket操作放在UI线程不然CNC没回包时界面就冻住。这里我习惯把Socket封装成一个CncChannel类把Open、Send、Receive、Close收在同一个类里业务层只跟CncReader打交道不直接碰字节流。3.3 原始值转业务值机械坐标换算与倍率解码数据读回来是一堆short/int直接显示是乱码数值。这一步要按手册做换算。常见换算有// 假设坐标寄存器的最小单位为0.001mm double xMM rawX / 1000.0; // 倍率常以编码值返回需要按对照表解码 int DecodeOverride(ushort raw, string model) { // 不同面板倍率编码区间不同这里用开关示例 switch (raw) { case 0x4000: return 50; // 占位示例具体查手册 case 0x4001: return 60; default: return 100; } }注意事项坐标值有正有负。寄存器可能是16位有符号读出来是ushort时要做符号扩展否则负坐标会显示成六万多。转速和倍率也一样先确认是有符号还是无符号再决定用什么类型解析。我惯用的做法是拿面板上的实际值和一个已知读数对照换算对了再往下写。这个步骤偷懒后面MES端全是清洗数据的麻烦。4. 跑通Demo的最小上位机连接配置、轮询线程与断线自愈前面封装好了通信层接下来要把它组织成一个能常驻运行的C#上位机。这个Demo程序应该有三层配置层负责读IP、端口、轮询间隔采集层负责在后台线程里按固定节奏发请求分发层把数据送给界面或MES。三层不拆开采集和界面耦合在一起将来加一台机床就是一场灾难。4.1 工程结构配置、读取器、轮询线程三层拆开我一般会在解决方案里建三个类CncConfig、CncReader、CncPollingService加上一个MainForm做展示。配置文件用JSON启动时反序列化这样换机床不用改代码重新编译{ Cnc: { Host: 192.168.1.10, Port: 6832, PollIntervalMs: 200, ConnectTimeoutMs: 3000, Items: [X, Y, Z, SPEED, FEED_OVERRIDE, ALARM] } }参数说明PollIntervalMs默认200毫秒够一般展示用低于50毫秒会让CNC的通信负担明显上升除非有明确需求不要追极限。Items数组是你关心点的清单CncReader按这个清单组装读取请求。把配置外置到JSON后现场调试时改IP不用动工程对车间维护人员友好很多。4.2 轮询线程与数据分发不要阻塞UI线程轮询服务用一个独立线程跑循环读到数据后通过事件发布出来。先定义数据结构再写循环主体public sealed class CncSnapshot { public double X, Y, Z; public int SpindleSpeed; public int FeedOverride; public bool IsAlarm; public DateTime Timestamp; } public sealed class CncPollingService { private readonly CncReader _reader; private readonly int _intervalMs; public event ActionCncSnapshot DataArrived; public void Run(CancellationToken token) { while (!token.IsCancellationRequested) { try { var snap _reader.ReadSnapshot(); DataArrived?.Invoke(snap); } catch (Exception ex) { // 记录异常连续失败N次后触发重连 } Thread.Sleep(_intervalMs); } } }逻辑说明Thread.Sleep放在读取之后保证两次采集之间至少间隔固定毫秒数。DataArrived事件在后台线程触发界面订阅时要用BeginInvoke或async/await切回UI线程直接改控件跨线程会偶发崩溃。这个结构的好处是采集频率和展示频率解耦界面卡一下后台采集不受影响。参数说明轮询间隔一般取100到500毫秒。取100毫秒时界面上坐标看起来丝滑CNC也扛得住取500毫秒则数据有半秒延迟适合只做报表记录。如果现场要求毫秒级看主轴负载波形就得改走三菱的周期通知功能不能用轮询硬顶。注意轮询间隔低于50毫秒时要评估CNC是否有丢包或通信中断的迹象。真到了这种需求先确认协议手册里的最小采样周期再决定方案。4.3 断线重连三菱连接数限制下的自愈策略CNC侧对连接管理比普通TCP服务端更严格常见问题是断线后马上重连失败因为服务端还认为上一个会话没断。我惯用的做法是连续读失败3次后先Close旧连接等待1到2秒再重新Open。不要做无限快速重连会把CNC通信模块搞出假死状态。public void EnsureConnection() { if (_connected) return; _native.Close(_subch); Thread.Sleep(1500); // 给服务端释放会话的时间这个等待值也算玄学但有效 _subch 0; int rc _native.Open(_host, _port, 3000, ref _subch, _clientId); _connected rc 0; }逻辑说明Sleep 1500毫秒是现场调出来的经验值太短CNC不认太长影响恢复时间。Open前把subch清零再传引用避免把旧的无效信道号传给服务端。重连成功后最好重新SYNC一次这个Demo里同步失败会自动走EnsureConnection自愈。这章就是把这个Demo程序做成一个能丢在车间工控机上连续跑一周不重启的最小骨架。下一章我会把最容易翻车的几个点单列出来照着排查能省下一整天。5. 三菱CNC数据采集避坑清单连不上、零值、乱码等5个高频坑写三菱采集的这段时间里我把现场踩过的坑按“现象→原因→解决”整理成了一份清单。下面五条最典型照着排查能少走不少弯路。5.1 连不上机床和重连失败现象1C#程序里Open返回超时但机床IP能Ping通。原因机床侧以太网参数里没启用CNC通信功能或者端口不对Ping通只说明网络通不代表协议通。解决到机床参数界面找到“网络/接口”设置确认启用以太网功能确认端口常见6832然后用SDK自带的测试工具把连接跑通再回C#侧调。调连接顺序应该是测试工具→C#封装→业务代码倒过来查会很累。现象2程序运行几小时后断线重连失败。原因CNC侧同一时间只允许一个上位机连接旧连接没释放或者现场网线接触不良。解决按4.3节的EnsureConnection做延迟重连日志里记录每次重连时间。如果多台电脑同时连同一台机床要把工作电脑固定为唯一客户端。5.2 坐标全是零和倍率对不上现象读数能跑通但坐标始终是0。原因典型的是读错了地址把“相对坐标”当成“机械坐标”读或者坐标寄存器没使能。解决先拿手册附录的地址表找到机械坐标对应的地址编号再在面板上摇动一个轴看读数是否变化。能连上不代表地址对这一步必须人工对一遍。现象读到的进给倍率是“60”这种整数但面板明明显示的是80%。原因倍率寄存器返回的是编码值不是百分比。解决查该机型的编码对照表。三菱不同面板倍率编码区间不同有的0x4000对应50%有的对应100%。把这个关系做成上面的DecodeOverride方法写成配置文件换机型改配置不动代码。5.3 中文乱码与DllImport崩溃现象程序号和报警文本读回来全是“锟斤拷”或日文符号。原因CNC返回的文本是Shift-JIS编码日文Windows的处理方式C#默认字符串是UTF-8编码不一致。解决拿到原始字节后显式转换Encoding.GetEncoding(932)“932”是Shift-JIS在.NET里的代码页。注意前提是程序能跑在Windows上Linux服务器上要先注册代码页。现象调用DLL的瞬间进程崩溃异常都来不及捕。原因动态库位数和进程位数不匹配或者函数签名和真实导出名不一致。解决先用DependencyWalker或dumpbin查看DLL导出函数名核对EntryPoint再把C#工程的Platform目标改成x86或x64和DLL对齐。这个坑最容易掩藏在一堆眼花缭乱的报错里所以拿到SDK第一件事就是做位数对齐。这五条都是我在现场真实翻车过的其中倍率编码和重连等待这两条几乎每台新机床第一次接入都会遇到。排查顺序建议是先确认能连再确认地址对再谈数据换算最后才优化稳定性和代码结构。6. 从Demo到产线数据服务用队列把采集值稳定送进MESDemo跑通只是第一步真正值钱的是把它从“能读”变成“能稳定读、能送数”。这里我分享一个最简单的升级把轮询和入库解耦。6.1 用ConcurrentQueue把“轮询”改成“生产-消费”采集线程只负责把快照塞进队列另一个入库线程负责攒批写入数据库。这样即使MES侧数据库慢了一下采集线程也不会被拖住。示意代码private readonly ConcurrentQueueCncSnapshot _queue new(); // 采集线程里 private void OnDataArrived(CncSnapshot snap) { _queue.Enqueue(snap); } // 独立入库线程 private async Task FlushLoop(CancellationToken token) { while (!token.IsCancellationRequested) { if (_queue.TryDequeue(out var snap)) { await Db.InsertAsync(snap); // 单条写也可以批量更快 } else { await Task.Delay(200, token); } } }参数说明队列累积量要监控如果短时间内积压上千条说明下游写不动了就要降轮询频率或者升级数据库结构而不是无限等待。批量入库一般以50条或2秒为窗口两者先到先写。6.2 多机床接入与落库从Demo程序到常驻服务多台机床接入时每台机床启动一个CncPollingService实例但连接数和线程数都要限制避免一台工控机同时开二三十条线程。常见做法是采集层共享一个线程池连接管理各自独立。落库方面建议直接把快照按时间戳全表保存给“机床ID时间”建索引后面做OEE和利用率分析都靠这张明细表。MES要汇总时再按分钟/小时聚合不要在采集线程里做聚合计算。我在第一台机床上线时吃过的亏是当时觉得采集程序只是个Demo日志随便写写后来半夜断网重连第二天想看历史记录什么都没有。现在我的习惯是凡是产品化的采集服务第一版就做好三件事时间戳校准和机床时间对表、重启自恢复、日志留够30天。这三点比花哨的界面有用得多。这个方向做下来三菱这台机器你搞通了再去接其他品牌CNC方法论是一样的先查协议手册再做最小闭环最后考虑稳定的数据通路。希望帮到你。本文还有配套的精品资源点击获取