简介:面向Fanuc数控系统二次开发与数据采集场景,这份Focas开发包提供了完整的函数功能讲解体系。内容覆盖Focas库主要API的用途、参数含义与调用逻辑,涉及的函数模块包括轴控制、主轴状态、动态数据、历史数据等常见采集场景;既有英文和日文的XML函数说明文档,也有按中文汇总整理成的Word资料,并附带C#代码示例,方便在.NET环境下直接参考或复用。压缩包约26.45MB,共2000个文件,主体为xml、htm、doc、dll、h、cs等类型,其中dll与h支持底层调用,cs演示具体采集逻辑,xml/htm则承担函数检索与说明,整体结构清晰,适合初次接触Focas或从事机床联网项目开发的技术人员。已有1432人学习下载,属于Fanuc数据采集方向较为实用的参考资源,可帮助读者快速梳理函数关系、缩短接口调试周期,对传感器接入、伺服状态读取等项目开发具有直接参考价值。
1. Fanuc机床Focas开发包:把数控系统的数据接口打开
车间里上了Fanuc系统的机床,想接MDC、做OEE,大概率会卡在同一个环节:坐标、程序、报警这些数据到底怎么从控制器里取出来。Focas(FANUC Open CNC API Specifications)就是发那科数控系统对外开放的开发包,它允许外部程序通过以太网与机床控制器建立连接,然后以一组C风格API读写数据。开发包解决的是设备联网最底层的通信问题,适合设备集成商、车间信息化工程师和给MES/DNC做对接的开发人员。这篇按真实集成交付的顺序展开:先讲开发包的结构和选型,再跑通最小连接程序,然后覆盖坐标、宏变量和程序的读取,最后把连接失败、数据错位、句柄占用这些最常见的坑逐个排掉。
2. 搭建Focas开发环境:从文件组成到接通第一台机床
2.1 一份Focas开发包的文件组成与语言选型
拿到Focas开发包,先认三样东西:fwlib32.h(API声明)、fwlib32.lib(Windows导入库)、fwlib32.dll(运行时库)。Linux环境下对应的是libfwlib32系列动态库和同名头文件,使用方式大同小异。不需要关心它内部怎么实现,你可以把它理解成数控系统开放的一个“设备侧网关”,连接成功之后,后续所有读写命令都通过一个句柄来完成。
Focas目前有两条技术线:FOCAS1走串口或早期I/O链路,FOCAS2走以太网TCP/IP,绝大多数新项目都是FOCAS2,默认端口是8193。选型时有个容易忽略的点:部分机床的FOCAS2功能选项没有开通,代码写得再对也连不上,这类问题会在第4章的避坑环节专门展开。语言选择上,没有绝对标准答案,但可以根据交付场景来判断。官方样例绝大多数是C/C++,所以用C启动最快;C#通过P/Invoke调用fwlib32.dll,做界面和日志更方便;第三方封装包上手快,但需要自己评估维护节奏。
| 语言/方案 | 上手速度 | 适合场景 | 主要注意点 |
|---|---|---|---|
| C/C++ | 中等 | 常驻采集服务、协议封装 | 句柄与内存需要自己管好 |
| C# / VB.NET | 快 | 中小型采集面板、快速交付 | P/Invoke声明必须与头文件严格对应 |
| 第三方封装 | 最快 | 原型验证、小规模对接 | 版本跟随和缺陷修复依赖作者维护 |
我一般会建议:数据量大、并发高、跑在工控机上无人值守的项目选C或C++;如果只是给车间做工具型软件,采集点也就几十台,用C#能省掉不少开发时间,代价是发布机器上要带对应运行时。
2.2 最小连接程序:三分钟和机床握个手
连接是Focas开发的第一步,也是最容易让人怀疑人生的第一步。先写一个只做连接和断开的程序,业务逻辑都不要加,确认通信链路通,再往里面填采集代码。
#include <stdio.h> #include "fwlib32.h" int main(void) { unsigned short flibhnd = 0; /* 会话句柄,由连接函数写回 */ char ip[16] = "192.168.1.50"; /* 机床侧配置的IP地址 */ unsigned short port = 8193; /* FOCAS2默认端口 */ short timeout = 10; /* 连接超时,单位秒 */ /* 建立以太网连接;返回EW_OK(0)表示成功,非0是错误码 */ short ret = cnc_allclibhndl3(ip, port, timeout, &flibhnd); if (ret == EW_OK) { printf("connected, handle=%d\n", flibhnd); /* 后续所有读写都放在这个句柄的生命周期内 */ cnc_freelibhndl(flibhnd); } else { printf("connect failed, ret=%d\n", ret); } return 0; }这段代码只做了两件事:发起连接、断开连接。cnc_allclibhndl3是FOCAS2以太网连接的入口函数,传入机床IP、端口和超时时间,成功后把句柄写入flibhnd;断开时调用cnc_freelibhndl,传入同一个句柄。每次连接成功后,句柄值会变化,调试时把它打印出来,方便确认是不是复用了旧的句柄。
参数说明里有几个细节需要留意。ip必须是纯IPv4字符串,不能带“http://”或者端口后缀。port默认就是8193,只有机床侧改过端口时才动这个参数。timeout单位是秒,单台调试推荐10到15秒;批量扫描车间时改成3到5秒,避免一台离线机床让整条轮询卡住。flibhnd初始化为0,如果在程序里反复连接,必须先释放旧句柄再申请新的,不能直接覆盖。
2.3 连接前必查的机床侧清单:IP、端口、选项与防火墙
常见做法是先把机床面板上的通信参数调出来确认,不要一上来就怀疑代码。Fanuc系统的以太网参数一般在“设定”与“诊断/以太网”相关画面里设置,部分机型改完还需要重启系统才能生效。和电气确认三件事:机床IP和子网掩码是否已设定;FOCAS2选项是否开通;这个IP在车间网络里有没有和其他设备冲突。
我的习惯是把面板参数拍照存档,再在PC上用最笨的办法验证链路。先用ping确认IP通不通,再用telnet 192.168.1.50 8193确认端口是否监听。连接立即被拒绝,通常说明防火墙拦截或端口不对;长时间无响应,则可能是机床侧FOCAS服务没起来。端口能正常握手,再跑2.2的最小连接程序,减少无效排错。
PC这一侧最容易栽的不是防火墙,而是多网卡路由。采集电脑上往往同时有管理网口、设备网口和无线网卡,系统路由可能把发往机床网段的包送到了错误网卡。处理办法是给设备网卡所在网段配置静态路由,或者临时禁用其他网卡做排除。记住一个原则:先确认网络路径是对的,再动代码。
提示:FOCAS接口单次请求通常都在毫秒级。调不通时不要用100毫秒的间隔疯狂重试,那是在给自己刷日志。把等待时间放长,逐项排查。
2.4 工程化接入:把fwlib32链接进现有项目
连接跑通之后,第一步就是把它固化到工程里。Windows上用CMake接入fwlib32.lib的配置大致如下:
cmake_minimum_required(VERSION 3.16) project(focas_collector C) add_executable(collector main.c) target_include_directories(collector PRIVATE ${FOCAS_INC_DIR}) target_link_libraries(collector PRIVATE ${FOCAS_LIB_DIR}/fwlib32.lib)逻辑说明:FOCAS_INC_DIR指向存放fwlib32.h的目录,FOCAS_LIB_DIR指向存放fwlib32.lib的目录。编出来的exe运行时需要找到fwlib32.dll,最简单的方式是把dll放到exe同目录或加入PATH。Linux下链接libfwlib32.so时注意同名字段,其他逻辑一致。
这里有一个非常容易翻车的点:库位数必须与程序位数一致。32位程序配32位库,64位程序配64位库,混着链接编译能过,运行起来各种奇怪错误。做完这步,连接层已经打通,下一章开始写真正的采集逻辑。
3. 用Focas读核心数据:坐标、宏变量与程序的调用写法
3.1 读实时坐标:轴序和坐标系先要对齐
读坐标是Focas采集最基础的函数,也是验证连接是否真正可用的试金石。核心调用是cnc_rdposition,它返回一个结构体,里面包含轴数、绝对坐标、机械坐标和相对坐标等数组。传-1表示读取全部轴,也可以传指定轴号读单轴。
ODB pos; /* 坐标结构体,包含axis、absolute、mach等字段 */ short ret = cnc_rdposition(flibhnd, -1, &pos); if (ret == EW_OK) { printf("axes=%d\n", pos.axis); for (int i = 0; i < pos.axis; i++) { /* absolute是绝对坐标,mach是机械坐标,按轴数组输出 */ printf("axis %d abs=%.3f mach=%.3f\n", i, pos.absolute[i], pos.mach[i]); } } else { printf("rdposition failed, ret=%d\n", ret); }这段代码先检查返回码,再遍历轴数组。pos.axis表示控制器实际返回的轴数,循环上限必须用它,不能写死成3或4。绝对坐标和机械坐标的含义在数控加工里差异很大:绝对坐标是工件坐标系下的当前点,机械坐标是机床坐标系下的位置。采集系统做OEE分析时通常关注绝对坐标,做行程保护或回零检测则关注机械坐标,选错会让数据在业务层面完全失真。
参数上有一个常被忽略的坑:传入-1虽然最省事,但返回的轴顺序是由系统通道配置决定的,不一定永远是X、Y、Z。车床上会出现C轴,五轴机床上会有附加轴,代码如果把“第一轴”写死成X,数据就会错位。我的做法是测试阶段把每个轴的坐标都打印出来,对照机床面板确认顺序,再固化解析逻辑。
3.2 读宏变量:把#100、#500当作数据通道用
Fanuc系统的宏变量非常适合做外部数据交换,比如把测量结果写入#100系列,或者通过写#500系列变量把刀具参数送进机床。读宏变量的API是cnc_rdmacro,一组变量连续读取,效率远高于逐个读。
#define MACRO_START 100 #define MACRO_COUNT 20 ODBM data; /* 宏变量结构体,data.value保存数值 */ short ret = cnc_rdmacro(flibhnd, MACRO_START, MACRO_COUNT, &data); if (ret == EW_OK) { for (int i = 0; i < MACRO_COUNT; i++) { printf("#%d = %.3f\n", MACRO_START + i, data.value[i]); } } else { printf("rdmacro failed, ret=%d\n", ret); }cnc_rdmacro传入起始宏变量号和期望读取的数量,返回值依次放入data.value数组。数值在结构体里是浮点数,打印和入库都方便。写入操作对应cnc_wrmacro,参数结构与读取基本一致,只是把ODBM里的值填好再调用。
用宏变量做采集有一个需要和电气确认的禁忌:不是所有宏变量都允许外部读写,系统参数会对变量范围做限制。另外,有些机床的梯形图程序会周期性写入同一批宏变量,上位机和PLC同时写会互相覆盖。投入使用之前,先确认哪些变量是明确“留给上位机”的,哪些是PLC在用的,避免两边打架。
3.3 读程序与报警:DNC和诊断面板的主干数据
DNC方向最常见的需求是读当前程序号和程序内容。读程序号用cnc_rdpgmnum,读程序本体用cnc_rdprogram。后者返回的不只是纯文本,还可能包含注释、跳过块等格式信息,而且返回的是带长度的数据结构,不能当作普通C字符串直接处理。
ODBP prog; /* 程序结构体,data是文本缓冲区,length是有效长度 */ short ret = cnc_rdprogram(flibhnd, 1024, &prog); if (ret == EW_OK) { printf("program length=%d\n", prog.length); fwrite(prog.data, 1, prog.length, stdout); }传入的第二个参数是缓冲区期望长度,prog.length返回实际数据长度,读取时按长度处理可以避免缓冲区越界。需要说明的是,程序内容里可能包含中文注释或特殊字符,Windows控制台直接打印偶尔会出现乱码,这不是采集错误,是代码页不一致,入库前统一转成UTF-8就好。
报警读取是设备诊断面板必须的功能。cnc_rdalm可以列出系统报警、伺服报警和机床报警,与坐标、宏变量组合在一起,就构成一个完整的设备快照。报警文本同样可能包含中文或日文,处理编码的规则与程序读取一致,先对齐字符集再落库,否则看板上的报警信息会变成一串问号。
3.4 放进采集快照:单位、时间戳和一次读一组
真正入库的数据,我更习惯在采集层做一个快照结构体:机床号、采集时间、绝对坐标、机械坐标、当前程序号、报警数、关键宏变量。一组API调用形成一个原子记录,同时写入日志或数据库。这样做的原因是,分开采集会遇到时间碎片,比如读坐标和读报警之间机床已经报警清零了,记录就会错位。
单位问题要在最开始就确认。Fanuc系统可以设置显示单位是毫米还是英寸,这个参数会影响API返回值的单位体系。测试时拿量块或千分尺对比一下采集值与面板显示,别等报表做出来了才发现单位不对。时间戳尽量以机床侧时钟为基准,或做统一的时钟同步策略。车间数据如果和MES工单时间对不上,后续排产分析会全部失真。我之前的教训是同时记录PC时间和机床时间两列,结果故障倒查时两套时间对不齐,后来改为统一以机床时钟为主、PC时间只做参考,问题才理顺。
4. Focas开发避坑指南:连接失败、数据错位与句柄泄漏
4.1 刚开机连不上、过几分钟又能连上?先等机床醒过来
现象:机床冷启动完成,面板显示正常,采集程序立即连接却失败;等几分钟后再运行同一个程序,又能连上。这种情况最容易让人怀疑是IP配置问题,其实不是。
原因:Fanuc系统启动时,以太网和FOCAS服务并不与面板显示同步就绪。选项加载、网络服务初始化有先后顺序,急停状态下部分外部接口也可能不响应。
解决:连接逻辑不要做成“一次失败就报告故障”,要设计成“握手-重试-容忍”。我一般先连接一次,失败后等1到2秒重试,连续试5到10次;连接成功后马上读一次机床状态当作握手。这是血泪经验,早期我在一个车间调试时用100毫秒间隔疯狂重连,日志刷了几千行,机床侧服务还没起来,最后是把重试间隔改成2秒才连上。
4.2 坐标对不上?轴序、坐标系和单位三个一起查
现象:采集程序运行正常,返回码一直是0,但X轴数值和面板显示不一致,或者X和Y互换了,又或者坐标整体偏移一个固定值。
原因:绝大多数是把机械坐标当绝对坐标读了,或者轴序受通道配置影响与物理轴不对应。系统显示单位如果设置成英寸,而你按毫米解析,数值也会差一个换算系数。
解决:先到机床面板把显示切到“绝对坐标”,再把代码里cnc_rdposition返回的每个轴都打印出来,人工比对一遍;然后手动走一个轴,看打印数据里哪个值跟着变。有人说这问题很玄学,其实不是,就是坐标系没对齐。单位问题用标准量块验证,确认转换系数后再写死。
4.3 句柄没释放,会话被占满,只能重启机床
现象:采集服务跑了一个上午,机床突然连不上了。重启采集程序也没用,重启机床才能恢复,而且过一阵又复发。
原因:大概率是代码路径里丢了cnc_freelibhndl。比如重连逻辑直接再次调用cnc_allclibhndl3申请新句柄,旧句柄从未释放;或者某个异常分支直接 return,把句柄漏在了机床侧。机床侧的会话数量是有限的,占满之后就会拒绝新的连接请求。
解决:在连接成功后打印句柄值,每次重连前确认旧句柄已释放;C++用RAII包装句柄、C#用IDisposable包装,确保所有异常路径都能释放。调试时按Ctrl+C终止程序后,句柄在机床侧可能还会保留一段时间,马上重连会被拒绝,等几秒再连。句柄泄漏没有后悔药,只能把释放逻辑焊死在每一条返回路径上。
4.4 多线程一起读就乱?给通信加把锁
现象:开多个线程分别读取坐标、报警、宏变量,偶发返回非0错误码,或者读到的数据明显不对;改成单线程后一切正常。
原因:Focas的会话句柄不是为高并发自由调用设计的。同一进程内多个线程同时通过同一个句柄下发命令,响应会交错,拿到的数据自然对不上。
解决:经典做法是给句柄访问加互斥锁,让所有API调用串行化。我这里补充一句:锁的粒度不能太小,尽量把一次业务读取(坐标+报警+宏变量)当成一个整体加锁,避免读序列被其他线程插入。如果确实需要高吞吐,让每台机床独占一个句柄、一个线程,甚至一台机床一个独立进程,相互之间不共享句柄。多线程能提升一点吞吐,但共享句柄带来的排查代价,通常比省下的时间贵得多。
4.5 返回码只判0不够:超时和缓冲长度要看怎么配
现象:程序偶发返回超时错误,但机床面板操作正常;或者读程序内容时,返回的内容明显截断。
原因:FOCAS调用里有两个隐藏参数容易被忽略:超时设置和缓冲区长度。连接时的timeout只作用于连接阶段,部分读写命令有自己的超时行为;cnc_rdprogram传入的缓冲区如果小于实际程序长度,数据会被截断,返回码可能成功也可能报长度不足,取决于具体实现。
解决:所有读写调用都检查返回码,非0错误码不要直接吞掉,把错误值打到日志里。读程序时先传一个小缓冲区试一次,根据返回的长度信息动态扩容,或者直接用足够大的缓冲区(如64KB)覆盖大多数程序。现场偶发的超时错误,建议记录重试而不是立刻判定机床故障,机床在执行某些内部操作时,外部接口应答会变慢,这属于正常现象。
5. 进阶与验证:断线重连、多机床轮询的数据怎么才算真对
5.1 断线重连与心跳:让采集服务不要“假活”
采集服务最怕的不是断网,而是断了之后进程还在跑,看板上数据却已经不再更新。我习惯给每台机床维护一个状态机,连接、采集、失败、退避、重连,用一个统一框架收口:
int attempt = 0; while (running) { short ret = cnc_allclibhndl3(ip, port, timeout, &hnd); if (ret != EW_OK) { sleep(backoff(attempt++)); /* 指数退避:1s,2s,4s...封顶60s */ continue; } attempt = 0; while (running && read_status(hnd) == EW_OK) { collect_snapshot(hnd); /* 按3.4的方式采集快照并写日志 */ sleep(interval); } cnc_freelibhndl(hnd); /* 退出内层循环后必须释放句柄 */ }read_status是封装的一个常规读状态命令,当它返回非0时说明链路已经断开,退出内层循环走重连逻辑。指数退避可以避免多台机床同时掉线时产生风暴式重连请求。日志里每一条重连都打点,保留恢复过程证据。
5.2 用“面板对账”验证数据可信
验证方法不复杂:一个人站在机床面板前手动摇轴,另一个人盯采集日志,每5秒记录一次绝对坐标。跑半小时后用命令检查采集日志里有没有异常跳变。
# 采集日志格式: 时间 ip axis_x axis_y axis_z,检查X轴跳变 awk 'NR>1 && $3-prev<-0.01 {print "jump at", NR} {prev=$3}' collect.log摇轴时坐标应该是连续变化的,出现超过0.01毫米的跳变说明读取的轴或者单位有问题。同一批数据连续半小时和面板对得上,再往OEE看板上送。我给自己定的规矩是任何新机型接入都要先过这半小时对账,防止上百台设备接入后假数据污染看板。希望帮到你。
本文还有配套的精品资源,点击获取