FOCAS协议开发实战:从C语言采集到PMC信号读写
发布时间:2026/10/27 23:39:28 锦皓数字建站

简介面向FANUC数控系统二次开发人员这份压缩包围绕FOCASFANUC Open CNC Access System协议展开解决上位机与发那科CNC设备通信难的问题可应用于远程监控、数据采集、程序管理、参数设置和报警处理等场景。FOCAS基于HTTP与XML支持TCP/IP网络通信开发者可借助标准编程语言快速构建客户端也适合先从文档了解协议原理再动手编码。包内共1627个文件压缩后约6.22MB其中705个xml和699个htm构成了协议文档与API说明的主体覆盖函数定义、数据结构、调用流程和网络配置说明169个gif图示直观展示交互过程另有12个dll、11个h、10个bas、1个lib等动态库、头文件与VB示例模块以及少量xsl/txt/css辅助文件便于直接调用或对照学习。已有1598人学习下载。开发者可按文档步骤配置网络参数参考基础示例实现连接、读写、报警处理等操作再根据实际需求扩展代码快速完成数控系统集成项目开发提升产线自动化水平。1. 从采集需求说起FOCAS 协议为什么值得用做产线设备联网时最常碰到的需求就是把 FANUC 数控系统的实时数据拿上来主轴转速、各轴坐标、倍率、报警号、PMC 信号。机床侧没有现成的 MQTT 或 OPC UA 服务简单 TCP 抓包又抓不到内部变量这时候“FANUC FOCAS 协议开发包”就是那条最直接的通道。发那科把这套接口封装成一组 C 函数库通过以太网口或 HSSB 总线连接 CNC函数命名一眼能看出用途上手成本比想象中低。这个方案解决的是数据采集层从“无”到“有”的问题适合做 MES 采集、设备状态监控、PMC 二次开发的工程师。后面的步骤多数照抄就能跑通坑主要集中在选型、权限和调用时序上。2. 先看懂 FOCAS 的通信结构和开发包以太网口怎么连、库从哪来2.1 FOCAS 是“函数库”不是“报文协议”和裸 TCP、OPC UA 的本质区别不少第一次接触 FOCAS 的人会先抓包想从以太网报文里解析出数据格式这是最常见的弯路。FOCAS 的全称是 FANUC Open CNC API Specifications它本质上是一套由发那科提供的 C 语言函数库通信层已经封装好了。你调cnc_rdspindle读主轴转速库内部自己组帧、校验、应答你拿到的只有结构体里的数值和返回值。把 FOCAS 当成“带文档的 TCP 服务”更合适而不是“自己定制协议格式”。和 OPC UA 相比FOCAS 的定位不一样。OPC UA 解决的是多厂商设备统一建模的问题服务器端和客户端都要理解信息模型FOCAS 则直接暴露 CNC 内部地址和数据字典读 PMC 的 G 地址、F 地址就像直接看梯形图里的信号点几乎没有语义映射成本。代价是它只认 FANUC适配面窄。同一条产线上如果你又遇到西门子、三菱那就得再配别的采集通道FOCAS 解决不了跨品牌的问题。常见的开发包分两类一类走以太网对应fwlib32.dll那套库适合绝大多数产线改造场景另一类走 HSSB 高速串行总线需要机床侧插专用板卡适合老机型或对实时性要求极高的场合。选型上优先以太网部署简单不占机床额外 IO而且 30i/31i/0i 系列基本都支持。拿到开发包后先别急着写代码第一件事是确认机床侧参数 1410 号或者其他网络参数里有没有把以太网口分配给 FOCAS 使用很多“连不上”就是这一步没做。2.2 拿到开发包后先确认四样东西库文件、头文件、说明书和选项功能开发包下载下来是个压缩包解压后内容各家渠道略有差异但核心四样东西一般不会缺。第一是头文件Windows 下最常见的是fwlib32.h里面定义了所有函数原型、结构体、宏常量写代码时几乎离不开它第二是导入库和动态库Windows 下对应fwlib32.lib和fwlib32.dllLinux 下是对应的.so或静态库第三是 FOCAS 说明书通常是一份几百页的 PDF里面每个函数有详细参数说明、返回值和错误码表第四是示例代码有些开发包自带简单的 C 例子有些需要去发那科官网单独申请。很多人在“选项功能”上栽跟头。FOCAS 库本身提供上百个函数但某个函数能不能用取决于 CNC 的系统版本和购买的功能选项。比如读 PMC 信号、写 PMC 信号、读刀具寿命数据这些在部分 0i 系统上要么需要额外选项要么被系统版本限制。处理办法很笨但很有效先调cnc_getversion拿到系统软件版本再对照说明书里的“功能与版本对应表”确认目标函数是否支持。提示开发包里的说明书错误码表一定要留着。FOCAS 返回值是负数比如常见的“功能未提供”“连接拒绝”不同数值对应不同原因排查时第一条就是查错误码表。2.3 连接与断开的固定节奏cnc_allclibhndl / cnc_open / cnc_closeFOCAS 的连接流程是一套固定套路和数据库连接有点像先建立库句柄再用句柄打开 TCP 连接用完后断开并释放句柄。三个关键函数是cnc_allclibhndl、cnc_open和cnc_close。cnc_allclibhndl负责初始化并返回一个句柄同时可以设置连接超时时间cnc_open接受 IP 地址、端口和超时参数cnc_close断开连接。端口号默认是 8193这是 FOCAS 以太网通信的固定端口不需要猜测也不要随意改。连接超时一般设 3 到 5 秒比较合适太短在机床负载高时容易超时失败太长会让上位机线程卡死。还有一点要注意大部分 FANUC 系统同一时间只允许一个 FOCAS 客户端连接如果机床上有其他软件已经占着连接你的应用就会连不上。老工程师的一台笔记本同时开了两个采集程序互相抢连接就是典型翻车现场。#include fwlib32.h #include stdio.h int main(void) { unsigned short handle 0; /* FOCAS 库句柄 */ short ret 0; /* 返回值 */ char ip[] 192.168.1.10; /* CNC 以太网 IP */ /* 1. 初始化库句柄3 秒连接超时 */ ret cnc_allclibhndl(ip, 3, handle); if (ret ! 0) { printf(库初始化失败错误码: %d\n, ret); return -1; } /* 2. 打开以太网连接端口用默认 8193超时 3 秒 */ ret cnc_open(handle, ip, 8193, 3); if (ret ! 0) { printf(连接失败错误码: %d\n, ret); cnc_freehndl(handle); /* 失败也要释放句柄 */ return -1; } printf(连接成功\n); /* 3. 断开连接并释放句柄顺序不能反 */ cnc_close(handle); cnc_freehndl(handle); return 0; }这段代码里有两个细节容易忽略。第一是cnc_allclibhndl失败后不需要也不能再调用cnc_open但句柄可能已经被分配所以返回错误时直接结束即可第二是cnc_close和cnc_freehndl必须都调用一个负责断开 TCP一个负责释放库资源漏掉后者在长时间运行的采集程序里会积压句柄。超时参数的单位是秒cnc_open的第三个参数是端口号Windows 下的 socket 底层调用不同但这些对我们透明不用管。3. 用 C 语言跑通最小采集读主轴转速和绝对坐标3.1 搭一个最小可编译工程包含头文件、链接库写 FOCAS 采集程序我习惯用 C 或 CC# 也能做但 P/Invoke 声明结构体麻烦不如 C 直接。Visual Studio 里新建一个控制台应用配置分三步把fwlib32.h所在目录加进“附加包含目录”把fwlib32.lib所在目录加进“附加库目录”然后在“附加依赖项”里写入fwlib32.lib。如果用的是动态库编译时只需要 lib运行时保证 dll 和 exe 在同一个目录或系统 PATH 里。如果是 Linux 上位机常见做法是用 GCC 直接编译链接时指定-lfwlib32但前提是你从发那科拿到了对应平台的库。不同操作系统的库不通用Windows 的 dll 拿到 Linux 上没用这点不要踩坑。开发阶段不用急着连真机先让工程编译通过再逐步加调用。# Linux 下编译 FOCAS 采集程序的常见写法 gcc -o focas_demo focas_demo.c -I./fwlib32/include -L./fwlib32/lib -lfwlib32 -lm这段命令的-I指定头文件路径-L指定库文件路径-lfwlib32链接 FOCAS 库。如果你的开发包里库名不是libfwlib32.so这种形式先解压看实际文件名再改链接参数。我遇到过拿到的是老版本fwlib32.dll在 Linux 上根本没法用最后只能换 Windows 工控机所以选平台前先确认库形态。3.2 建立连接并做版本握手为什么不能跳过 cnc_getversion连接成功后先别急着读数据调一次cnc_getversion做版本握手是值得养成的习惯。这个函数返回 CNC 的系统软件版本、系列信息写入一个ODB_VER结构体。作用是双重的一是确认通信确实建立二是拿到版本后可以判断后续某些函数能否使用。不同系统版本的 FOCAS 支持度有差异比如 0i-D 和 0i-F 对 PMC 读写的限制不一样先看版本再选函数能省掉不少排查时间。#include fwlib32.h #include stdio.h ODB_VER version_info; /* 版本信息结构体 */ short ret cnc_getversion(handle, version_info); if (ret 0) { printf(系统版本: %d.%02d\n, version_info.dummy2[0], /* 主版本号所在位置 */ version_info.dummy2[1]); /* 次版本号所在位置 */ } else { printf(版本读取失败: %d\n, ret); }结构体ODB_VER的定义在fwlib32.h里是明确的dummy2数组里存的就是版本号字节不要自己想当然改偏移。说明书里还写了cnc_getversion不需要机床处于特定模式手动模式也能调。这个函数也是后面排查“功能不支持”时的重要工具先知道系统版本再对照说明书的功能矩阵比反复试错快得多。3.3 读主轴转速 cnc_rdspindle结构体里哪个字段才是实际转速主轴转速是设备监控最常用的信号之一。FOCAS 提供cnc_rdspindle函数一次调用能同时拿到主轴程序指令转速、实际转速、倍率、负载等数据存放在SPINDLE_MSG结构体里。容易搞混的是“程序指令转速”和“实际转速”两个字段程序里写 S1000实际可能因为倍率开关打到 50% 而只有 500采集设备状态时应读实际转速字段。#include fwlib32.h #include stdio.h SPINDLE_MSG spindle; /* 主轴数据结构体 */ short ret cnc_rdspindle(handle, spindle); if (ret 0) { /* S1_ACT 是主轴 1 的实际转速单位是 min-1 */ printf(主轴实际转速: %d r/min\n, spindle.S1_ACT); /* S1_LOAD 是主轴负载百分比做预测性维护时会用到 */ printf(主轴负载: %d%%\n, spindle.S1_LOAD); }SPINDLE_MSG里的关键字段包括S1_ACT主轴 1 实际转速、S1_PDIR程序指令转速、S1_LOAD主轴负载。如果你机床是双主轴结构体里还有S2_开头的一组字段读取逻辑完全一样。有一个常见翻车点部分系统返回的转速单位是检测单位而不是转每分说明书里专门有说明需要根据机床参数 3716 号之类的检测单位设置换算。如果发现读出来的值比实际大了 10 倍或小 10 倍先想到单位换算。3.4 读绝对坐标 cnc_rdposition判断该用哪一个位置类型坐标读数比主轴转速略复杂因为 FOCAS 不只给一种“位置”。cnc_rdposition通过第一个参数指定位置类型相对坐标、绝对坐标、机械坐标、剩余移动量、伺服位置等。做设备状态监控通常读机械坐标因为它是机床坐标系下不经工件坐标系偏移的原始值做加工分析则可能要绝对坐标或剩余移动量。选错类型读出来的数跟机床画面显示对不上会让人误以为采数程序有 bug。#include fwlib32.h #include stdio.h ODB_POS position; /* 位置结构体 */ short ret cnc_rdposition(handle, 1, position); /* 1 表示绝对坐标 */ if (ret 0) { /* position.data[i] 里存的是各轴坐标具体轴号对应结构体中的索引 */ printf(X 轴绝对坐标: %d 检测单位\n, position.data[0]); printf(Y 轴绝对坐标: %d 检测单位\n, position.data[1]); }结构体ODB_POS的data数组按轴顺序存放坐标值但单位是检测单位而不是毫米或英寸需要换算。换算公式在说明书里写得很清楚如果是公制机床通常要乘上进给当量常见是 0.001mm如果机床开了分子分母驱动这个换算系数就必须看参数。我的习惯是把换算逻辑单独封装成一个函数后面换机床或者换轴配置只改系数不碰采集主逻辑。提示写采集程序时顺便把cnc_rdaxisdata也试一下。FOCAS 里读坐标有好几个函cnc_rdposition适合整组坐标单独读某一轴的数据用cnc_rdaxisdata更轻量二者返回值的单位逻辑不完全一样。4. 从只读走向控制PMC 信号读写与写回边界4.1 PMC 地址的层级关系X/Y/G/F 地址和梯形图的关系FOCAS 采集做到后面一定会碰到 PMC 信号读写。CNC 里梯形图程序操作的是 X外部输入、Y外部输出、G从 PMC 送给 NC 的信号、F从 NC 送给 PMC 的信号这些地址而 FOCAS 提供了cnc_rdpmc和cnc_wrpmc两个函数访问它们。这里的关键是理解地址不是“内存地址”而是梯形图里的软元件号。在 PMC 程序里G 地址通常是上位机或者 PMC 逻辑控制 NC 的通道比如进给保持、程序暂停、倍率信号F 地址是 NC 反馈给 PMC 的状态比如运行中、报警中、到位信号。你在梯形图监控界面上看到 G8.0 这样的信号点在 FOCAS 里调用cnc_rdpmc时同样指定地址类型和地址号就能读到。很多 MES 系统做“远程暂停”“远程启动”就是通过写 G 地址实现的但写之前必须搞清楚这个点位在梯形图里有没有被别的逻辑驱动。和 PMC 地址打交道前最好先打开机床的 PMC 梯形图监控页确认目标地址的注释和程序段。与其漫无目的地搜“fanuc 机器人指令大全 pdf”不如直接看手上这台机床的 PMC 程序注释信号名往往就在注释里。FOCAS 只是给你一扇读写窗口信号本身的语义是机床厂在梯形图里定义好的。4.2 cnc_rdpmc 和 cnc_wrpmc 的最小调用地址类型、长度和格式cnc_rdpmc的函数原型看起来略复杂实际参数就是“地址类型、地址号、读取长度、数据缓冲区”。地址类型用常量区分比如PMC_ADDR_TYPE_G、PMC_ADDR_TYPE_F、PMC_ADDR_TYPE_Y等。读取长度按字节算不是按位算读一个字节会把 8 个位一起读回来。最小调用示例#include fwlib32.h #include stdio.h short data[1024]; /* 数据缓冲区 */ short ret cnc_rdpmc(handle, PMC_ADDR_TYPE_G, /* G 地址 */ 8, /* 从 G8 开始 */ 1, /* 读取 1 个字节 */ data); /* 输出数据 */ if (ret 0) { /* data[0] 的低 8 位对应 G8.0 到 G8.7 */ printf(G8 字节值: 0x%02X\n, data[0] 0xFF); }这里的data是按 short 数组接收的但实际只用低 8 位说明书里的例子也是这么写的。读 F、X、Y 地址的写法完全一样只要换第一个常量。cnc_wrpmc的参数结构也一样区别是data变成输入参数把要写的值填进去。写单个位时最好先读后写把原字节读回来改其中一位再写回不然容易把其他位覆盖掉。PMC 信号读写最容易踩的坑在“读到的值和梯形图监控不一致”。原因通常是你用了错误的地址类型G 地址和 F 地址在画面里都显示为 G/F 开头但它们的 IO 映射完全不同FOCAS 里的地址类型常量选错读出来的数据自然不对。还有一点PMC 信号是字节寻址的不要试图跨字节连续读一大段长数据分段读更稳。4.3 写回时的三条边界梯形图覆写、厂家宏、KAREL socket 的岔路写 G 地址实现远程控制听起来很诱人但真正上线前要确认三件事。第一梯形图程序本身会不会在每个扫描周期重新计算这个地址。如果梯形图里有线圈一直驱动同一个 G 信号你通过 FOCAS 写进去的值会在下一个扫描周期被覆写表现为“写了没反应”这不是 FOCAS 的问题而是控制逻辑冲突。第二发那科系统里有“厂家宏”和梯形图交互的机制有些机床厂已经把远程启动、远程暂停做成了宏程序接口你直接写 PMC 地址反而绕过了他们设置的保护逻辑。碰到这种情况先看机床附带的技术资料里有没有宏程序调用说明能走厂家预留接口就优先走接口。第三如果需求是让 NC 主动上报数据给上位机而不是上位机轮询那 FOCAS 只能算一半方案。PC 侧调用 FOCAS 永远是被动轮询模式NC 侧主动发起 socket 通信是另一条技术路线发那科系统上常见做法是写 KAREL 程序配合 socket 函数在 NC 内部建立客户端连接。FOCAS 负责密集采数KAREL 负责事件触发上报各管一段比改 PMC 梯形图安全得多。5. FOCAS 开发常见问题排查连不上、读错值、程序卡死5.1 连不上能 ping 通但 cn c_open 超时现象PC 能 ping 通机床 IP但cnc_open一直超时返回值是超时类错误。原因有两类一类是机床侧 FOCAS 以太网功能没启用或者端口被占用另一类是连接已经被人占了。0i 系列机床的参数设定页里有专门的“以太网/FOCAS”设置项需要把 FOCAS 和嵌入式以太网功能分配好否则即使 ping 得通TCP 8193 端口也没有服务在监听。解决先拔掉可能占用的连接比如其他上位机软件再在机床 MDI 上检查以太网设定页和 FOCAS 相关参数。如果机床侧一切正常就抓包看 TCP 握手有没有完成SYN 发出去有没有回包。很多情况下是现场网段有防火墙或交换机端口隔离工控机到机床之间做了访问控制ping 通 ICMP 不见得 TCP 就通。5.2 读主轴转速一直是 0倍率位置和单位换算双确认现象连接正常cnc_rdspindle返回值是 0但S1_ACT恒为 0机床画面上明明有转速。原因通常是两个第一机床主轴倍率开关在 0% 位置实际转速本来就是 0这是数据对但业务判断错误第二读取的函数用错了FOCAS 里主轴数据有几个函数cnc_rdspindle读的是当前主轴状态有的系统需要先确认主轴编号如果主轴上电状态异常也会读到 0。解决先把倍率开关打到 100%确认机械动作正常再判断是通信问题还是数据问题。再用机床诊断页面核对主轴诊断号的实际值如果诊断号有值而 FOCAS 读出来是 0就要检查检测单位换算和主轴号参数。我的排查习惯是先背书上的数据字典再背程序的赋值语句顺序反了容易白查。5.3 读出来的坐标和画面差很多检测单位、当量与坐标系类型现象坐标读数能变化但值比机床画面大 1000 倍或者带乱七八糟的小数。原因基本锁定在检测单位换算FOCAS 返回的是“检测单位”不是显示单位。常见公制机床下 1 个检测单位等于 0.001mm但遇到有分子分母驱动的轴换算系数变成 0.001 乘以分子除以分母。坐标系类型选错也会造成偏差绝对坐标、机械坐标、相对坐标读数各不相同画面上显示的是“相对坐标”而你程序里读的是“机械坐标”两者自然不一样。解决把cnc_rdposition的位置类型参数从 1 到 5 都试一遍跟画面逐一对上。这个办法靠得住因为位置类型枚举在不同版本里基本稳定。然后把换算系数写成一个配置项别硬编码在代码里换机床时只改配置不改程序。5.4 程序跑久了卡死没有正确设置超时和线程模型现象采集程序刚启动正常跑几小时后界面卡死或者线程堆积。原因多数是 FOCAS 调用是阻塞式的你把它放在了 UI 线程里机床偶尔通信延迟几秒界面就卡住了或者循环里调用cnc_rdposition后没有检查返回值连接已经断开了还继续读库内部一直等超时。解决一是调用cnc_settimeout显式设置通信超时建议单次读取超时不超过 3 秒二是把所有 FOCAS 调用放到独立采集线程UI 和采集之间用缓存队列解耦三是循环里没读到的数据直接丢不要阻塞重试下个周期再读。FOCAS 的返回值是排查依据每次调用后把ret打出来卡死之前一定有先导现象。5.5 写 PMC 信号无效梯形图覆写和地址占用排查现象cnc_wrpmc返回 0但写进去的信号马上变回去或者机床完全没反应。原因优先级最高的是梯形图里该地址被线圈驱动PMC 每个扫描周期都按程序逻辑重算外部写入相当于“比赛跑步”永远跑不过扫描周期。其次该地址可能被系统设定为只读或者被别的程序占用。解决先在 PMC 梯形图监控界面里看这个地址有没有线圈和触点引用如果有要么改梯形图程序要么换一个没有驱动信号的备用地址。用诊断功能监控该地址一个扫描周期内的变化确认是不是被覆写。这个排查过程需要梯形图编程软件支持光靠 FOCAS 看不到梯形图内部逻辑要请现场电气工程师协助。症状优先排查项常见原因连接超时机床侧 FOCAS 设置、端口占用功能未分配、连接被占读数恒为 0倍率位置、主轴状态倍率为 0、函数选错坐标偏差大检测单位换算当量系数、位置类型程序卡死超时设置、线程模型阻塞调用、循环无超时写信号无效梯形图驱动、地址占用线圈覆写、地址只读6. 把 FOCAS 采集线程接进产线系统缓存、超时与上线验证到了这一步基本功已经够了真正决定这个方案能不能长期稳定跑的是线程模型和验证方式。我的习惯是一个 FOCAS 连接只属于一个采集线程绝不在多个线程里共用同一个句柄库内部没有为并发访问做完整支持。采集周期按设备类型区分普通状态监控设 500ms主轴负载、振动这类需要做趋势分析的设 100ms 以内但不要低于 50ms频繁轮询会加重 CNC 通信负载反而影响机床执行。线程之间用环形缓冲或消息队列传递数据采集线程只负责把数据塞进队列业务处理线程负责落库或转发。这样即使数据库偶发阻塞采集线程也不会被拖死。超时设置上cnc_settimeout我一般设 3 秒读取函数单次超过 3 秒就放弃本轮下一轮继续。上线前用 CNC 模拟器或直接在机床手动画面做三件事先核对主轴转速在倍率 100%、50%、0% 时的数据再核对零点附近各轴坐标与画面的误差最后连续跑 24 小时看有没有句柄泄漏或偶发超时。我在现场养成的习惯是每台机床写一个独立配置文件IP、端口、检测单位系数、PMC 地址清单全放进去绝不把参数写死在代码里。有一次换了一台 0i-F 机床坐标当量系数不同因为配置化做得好只改了配置就上线。FOCAS 开发包这套方案前期投入主要在权限确认和地址梳理上一旦跑通后续接 MES、做看板、上预测性维护都只是加函数的事。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。