简介:面向工业自动化开发者的数控机床数据采集示例程序,使用微软的编程语言编写,通过专用的机床开放接口读取运行状态与加工参数,适用于需要掌握机床联网通信、上位机监控以及设备数据整合的工程技术人员。压缩包为RAR格式,共有51个文件,整体大小为3.45MB,包含8个源代码文件、14个动态链接库、完整的工程解决方案与配置文件,可清晰看出项目的依赖关系和模块划分。目前已有90人学习浏览,属于聚焦核心场景的轻量级实例。程序自带可编译的完整工程,展示了网络连接建立、指令报文构造、返回数据处理以及异常处理等关键流程,并给出刀具、主轴等常见参数的读取演示,便于直接运行调试并在此基础上扩展更多采集功能,提高二次开发效率。
1. 项目背景与采集方案选型
1.1 为什么车间里都在聊FOCAS采集
做车间信息化或者设备联网的朋友,对FOCAS这个名字肯定不陌生。简单说,FOCAS是发那科(FANUC)数控系统提供的官方二次开发接口,全称叫FANUC Open CNC API Specifications。它把数控系统内部的坐标、主轴信息、报警、宏变量、加工程序这些数据,通过以太网或串口开放给外部程序读取。
我最早接触这个需求,是给一家机械加工厂做车间看板。老板要求在办公室大屏上实时显示每台加工中心的当前坐标、主轴转速、进给速度和刀具号。一开始想过用OPC UA网关,但机床那边是发那科系统,没有现成OPC Server,而且再加一层网关,成本和时间都上去了。后来发现直接用FOCAS库写采集程序,走以太网直连机床,既省了中间设备,又能在代码层面完全控制采集逻辑,于是就把方案定成了FOCAS+C#。
这个方案解决的核心问题,就是让机床不再是信息孤岛。机床状态、加工进度、报警信息,这些过去只能靠人跑去车间看的数据,现在都能实时进数据库、上看板、做统计,为后续的OEE分析和生产管理提供数据基础。
1.2 为什么选择C#写FOCAS采集
选C#的原因很直接:开发效率高,和Windows平台的对接方便,做上位机界面、写数据库、对接Web API都非常顺手。FOCAS官方库本身是用C/C++写的,通过DllImport在C#里调用,这个路子很成熟,网上资料也不少。
相比VC++开发,C#在处理字符串、数组、结构体转换时省心很多。比如读取机床返回的中文报警信息,C#里直接处理字节数组再按编码解析就行,不用像C++那样手动管理内存。相比Python,C#的打包部署更简单,发给现场人员直接一个exe就能跑,工业场景里这点很重要。如果你是在做正式的车间集成项目,C#是我比较推荐的选择。
2. 环境准备与FOCAS库的接入
2.1 FOCAS库的获取与版本选择
开发前必须先拿到FOCAS Library,这个库一般有两个来源:一是发那科随系统附带的光盘或资料包,二是找发那科代理商或技术支持申请开发包。库文件核心是FWLIB32.dll(32位系统)和FWLIB64.dll(64位系统),配套还有头文件focas32.h和相关的LIB文件。
这里有个关键坑:很多机床系统软件版本不同,FOCAS库的版本也有差异。比如0i系列和30i系列能用的函数基本相同,但个别功能(比如读取某些诊断参数)在旧库上可能不支持。我的建议是尽量用较新版库,然后在下位机(也就是机床那边)把以太网和FOCAS功能开通。发那科系统的“FOCAS2/Ethernet”功能需要在系统设定里打开,有些机床出厂默认就是开的,有些则需要通过参数或功能选项开启。
2.2 C#里的DllImport封装
拿到库之后,在C#工程里的第一步,就是建一个类来封装FOCAS的所有外部函数。这个类基本就是把focas32.h里的函数声明转成C#的DllImport方法。先看最核心的连接和断开函数:
public class FocasLib { [DllImport("FWLIB32.dll", EntryPoint = "cnc_allclibhndl3")] public static extern short cnc_allclibhndl3(string ip, ushort port, int timeout, out ushort handle); [DllImport("FWLIB32.dll", EntryPoint = "cnc_freelibhndl")] public static extern short cnc_freelibhndl(ushort handle); [DllImport("FWLIB32.dll", EntryPoint = "cnc_rdposition")] public static extern short cnc_rdposition(ushort handle, int type, int length, out PositionData position); }连接函数cnc_allclibhndl3里,ip就是机床的IP地址,port默认用8193(这是FOCAS2的默认端口),timeout按毫秒算,handle是返回的会话句柄。断开用cnc_freelibhndl,这个必须成对调用,否则机床那边的连接数会被占满。
2.3 连接参数与初始化流程实操
实际连接前,建议先ping一下机床IP确认网络通,然后在机床端确认FOCAS端口没被占用。下面是完整的连接初始化代码:
ushort handle = 0; short ret = FocasLib.cnc_allclibhndl3("192.168.1.50", 8193, 5000, out handle); if (ret == 0) { Console.WriteLine("连接成功,句柄: " + handle); // 读取机床信息 } else { Console.WriteLine("连接失败,错误码: " + ret); }我实测下来,返回0表示成功。返回非0值就要查错误码表,常见的有-17(句柄无效)、-20(操作保护)、-8(忙)。如果现场连不上,多半是IP没配对或者机床那边没开FOCAS服务,这个后面讲排查时细说。
3. 核心采集函数与参数类型说明
3.1 坐标位置读取的两种方式
读坐标是FOCAS最常用的功能。cnc_rdposition这个函数返回的是机床当前各轴的机械坐标、相对坐标和绝对坐标。函数里有个type参数,决定你要读哪组坐标:
// 定义坐标结构体 [StructLayout(LayoutKind.Sequential)] public struct PositionData { public short dummy; public short type; public short axis; public short dataMask; public double[] position; } public static short cnc_rdposition(ushort handle, int type, int length, out PositionData position); // 调用示例:读取机械坐标 short ret = FocasLib.cnc_rdposition(handle, 1, 8, out pos);type用1代表机械坐标系,用2代表绝对坐标系,用3代表相对坐标系。length是轴数,范围按实际机床来,一般填8就够用。返回的position数组是double类型,单位是毫米。还有一个细节:有些发那科系统坐标值带小数点和正负号,显示的时候最好统一格式化,比如.ToString("0.000"),这样在看板界面上才整齐。
3.2 主轴状态与负载的实时采集
主轴转速、负载、倍率这些数据,在设备监控里很重要。读主轴信息用的是cnc_rdspindle函数,返回的数据包括主轴实际转速、指令转速、负载百分比、主轴状态等。实际生产里,主轴负载能间接反映加工刀具磨损情况,所以很多工厂都盯着这个值。
[StructLayout(LayoutKind.Sequential)] public struct SpindleData { public short dummy; public short alarm; public short speed; public short load; public short maxSpeed; public short actSpeed; public short SCode; public short unit; public short type; } public static short cnc_rdspindle(ushort handle, int type, int length, out SpindleData data); // 调用示例 short ret = FocasLib.cnc_rdspindle(handle, 0, 1, out spindle);读出来的speed和actSpeed需要根据参数号来换算实际转速,这一点非常坑。发那科系统的速度显示单位有两种,有的机床按0.1转显示,有的按1转显示,具体要看系统里有没有开启小数位显示。我在现场遇到过读数翻十倍的情况,排查半天发现是速度单位没转换,所以拿到值以后一定先对照一下机床面板显示的数值。
3.3 报警信息的抓取方式
报警读取,对设备管理来说比坐标还重要。机器一停,就知道是哪个报警在干活。FOCAS里读报警的函数有cnc_rdalarm2和cnc_rdalmmsg两类,前者返回报警号和报警级别,后者返回报警文本信息。
public static short cnc_rdalarm2(ushort handle, int type, out AlarmData alarm); [StructLayout(LayoutKind.Sequential)] public struct AlarmData { public short dummy; public short type; public short alarmNo; public short axisNo; public short alarmSub; public short status; }要注意,type填0是读报警信息,填1是读操作信息,填2是读外部报警信息。报警类型需要按位判断,比如type返回的bit0代表是否有报警,bit1代表是否有紧急停机,bit2代表是否有外部报警。实际开发时,我通常会同时调cnc_rdalmmsg把报警文本也取出来,这样界面上能直接显示“1021 EMERGENCY STOP”,而不是一个光秃秃的报警号。报警文本函数返回的是字节数组,不同系统编码可能不一样,需要尝试用ASCII或者GBK解码,这个在界面显示中文报警时特别重要。
3.4 读取宏变量与自定义数据
有些场景工厂会在宏变量里存一些自己的数据,比如加工工件的批次号、当前的加工程序号、PLC写入的计数器值。这些数据通过cnc_rdmacro函数可以读出来。
public static short cnc_rdmacro(ushort handle, int macroNum, int length, out double[] macroData); // 读取#500宏变量 double[] macroValue = new double[1]; short ret = FocasLib.cnc_rdmacro(handle, 500, 1, out macroValue);这里有一个硬性要求:读取宏变量时,机床必须处于运行状态或者至少是数据服务器可访问状态,否则返回非0错误码。另外,宏变量号范围是1到999,但有些系统允许扩展到更大范围,具体要看机床参数设定。我在实际项目里,用宏变量做过一个很实用的功能:在每道工序完成时由PLC写#501增加计数,C#程序隔几秒读一次,就能算出每天的加工件数和节拍,比人工统计准得多。
4. 常见问题排查与实用经验
4.1 连接失败的排查清单
连接失败是最常见的问题。做个速查表,按优先级从高到低排查:
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| ping不通 | IP地址不对或物理断线 | 用机床面板确认IP,检查网线、交换机端口 |
| ping通但连接失败 | FOCAS服务没开通 | 进机床系统设定,确认FOCAS2/Ethernet功能为ON |
| 返回-17 | 句柄无效 | 每次连接后先检查返回值,不要把上次的句柄拿来复用 |
| 返回-20 | 操作保护 | 检查机床是否有外部程序在占用FOCAS通道,释放后重试 |
| 连接一段时间后断开 | 采集频率太高或超时设置太短 | 延长超时时间到5000ms以上,采集间隔至少控制在200ms以上 |
我在现场被“连接失败”卡过很久的一次,就是机床端的FOCAS服务被安全程序挡了。后来在发那科系统设定界面的“以太网”菜单里重新启用FOCAS服务,才恢复正常。如果现场机床是联网的,先确认没有别的上位机软件同时占用FOCAS连接,发那科系统同一时间只允许有限个FOCAS客户端接入。
4.2 数据读取异常与编码问题
坐标数据读取出来乱码或者数值跳变,通常不是网线问题,而是数据类型转换问题。C#里的int是4字节,short是2字节,而FOCAS的很多结构体成员是short类型(也就是C++的short),如果不按顺序对齐组织结构体,数据就会串位。最稳妥的做法是严格按照focas32.h里的定义来写C#结构体,字段顺序一个都不能改。
中文报警信息处理也是一个坑。发那科系统报警文本的编码不是统一的unicode,有些系统用Shift-JIS,有些用GBK,还有一些新系统用UTF-8。我的经验是:先尝试GBK解码,如果出来的汉字全是“锟斤拷”之类的乱码,再换Shift-JIS试一次。做个小的编码探测函数,把解码结果里是否包含常见汉字作为判断依据,实测下来基本能自适应。
4.3 采集程序的稳定性与性能优化
FOCAS采集程序一旦跑起来,就是要7x24小时稳定的。有几个关键点必须注意。第一,采集线程和界面线程要分离,不能把采集放UI线程里,否则界面一卡数据就跟着停。第二,连接句柄要用锁保护,多个线程同时读写同一个handle容易出问题。第三,采集频率要合理,不是越快越好,一般1到2秒采一次就足够看板展示和数据库存储了,太频繁反而增加机床系统的负担。
我通常的做法是:单独开一个BackgroundService或者Thread,循环里做采集、存库、更新缓存,界面只负责从缓存里读数据。采集线程通过CancellationToken控制启停,程序退出时先断开FOCAS连接再关线程,避免句柄泄漏。真正运行起来以后稳定很多,连续跑几个月不用重启。
5. 从采集到应用:数据能拿来做什么
5.1 设备状态看板与OEE统计
FOCAS采集不只是把数据读出来那么简单,真正有价值的是数据加工和应用。把采集到的坐标、转速、负载、报警数据落进数据库之后,可以做设备状态看板、OEE统计、产量报表、刀具寿命预测等。
最常见的一个玩法是“设备状态自动判定”。通过判断主轴是否在旋转、进给轴是否有移动、程序是否在运行,可以把设备状态归纳为“运行中”、“待机”、“报警停机”、“手动模式”等几个大类,再结合时间戳准确计算每台设备的OEE。这些数据再汇总到Excel或报表里,管理层每天一早就能看到昨天每台设备真正干了多少活。
5.2 结合宏变量做产量溯源
前面讲到的宏变量读取,在这个场景下特别有用。如果机床的加工程序每完成一件工件就通过宏程序把#501加1,那FOCAS程序读到的#501就是这台机床的累计产量。配上产品编号和班次信息,就能精确追溯每个工件的加工时间,甚至能定位到具体是哪台机床、哪把刀具、哪道工序出了问题。
这个方案比人工扫码记录靠谱得多,因为数据是机器自己报上来的,不依赖操作工自觉。操作工只需要每班开始时把设备号、班次号在程序或面板上设置好,剩下的事情全自动。
5.3 后续功能扩展方向
FOCAS能做到的远不止这些。再往后可以接程序上传下载,实现加工程序的远程管理;可以读刀具寿命数据,做刀具管理;还可以读伺服监控数据,做设备健康度预测。如果再搭配机床侧的传感器采集,整个车间的数字化底座就成型了。
从个人实践角度说,FOCAS采集项目最大的价值不是代码本身,而是把车间里每一台设备“连接”起来之后,整个管理方式的转变。以前靠老师傅经验判断设备状态,现在数据告诉你一切。代码写完只是第一步,真正难的是让这些数据在生产管理里落地、发挥作用。我始终觉得,能帮车间解决实际问题的系统,才是好东西。
本文还有配套的精品资源,点击获取