
简介Focas2开发包是一套面向法兰克FANUCCNC数控系统二次开发的接口工具集内含Fwlib32.bas接口模块与API接口文档适合设备集成商、工艺工程师及工业软件开发者用于机床编程控制、实时状态监控与加工数据采集。压缩包共6797个文件约25.68MB内容以xml、htm格式的帮助文档为主配合doc接口说明、dll动态链接库、h头文件、lib静态库、vb/bas示例工程和exe辅助工具组织清晰可快速定位所需接口说明或调用范例。包内同时涵盖Focas1与Focas2两代资料方便对照新旧API差异理解接口演进过程同时兼容旧设备开发需求。通过xml/htm文档和bas示例开发者能快速学习如何调用接口实现G代码下发、机床坐标读取与刀具信息获取对进阶用户dll/sys/vxd等底层文件可支撑更深入的通信调试与自定义功能扩展。已有2726人学习下载对希望吃透法兰克数控协议、搭建定制化生产管理系统的开发者来说这份开发包具有很高的参考价值。 做设备联网和数控机床数据采集的朋友应该都见过这样一个场景机床的控制面板前人挤人但中控室的大屏上却永远差一组实时数据。想拿到机床的真实状态最直接的路子就是通过发那科系统的focas2开发包。这套FANUC官方提供的以太网通信开发库是目前连接发那科系统CNC与上位机最主流、也最可靠的一条路。从读取机床坐标、主轴负载、报警信息到程序上传下载、PMC信号读写基本上一台机床需要对外提供的数据它都能给你。这篇文章不是官方文档的搬运工我就按自己实际做项目的经验把从开发包确认、环境配置、核心函数使用到踩坑排查的完整过程捋一遍。文章里穿插的代码示例、参数配置和问题案例都是我在真实机床上跑过的。尤其适合刚接手设备联网、数控机床数据采集、MES对接这类项目的朋友哪怕你之前没碰过FOCAS2按这个思路走也能快速上手。1. 从车间大屏到FOCAS2为什么工业上位机开发绕不开它1.1 FOCAS2到底是什么FOCAS2的全称是FANUC Open CNC API Specification 2直译过来就是“发那科开放CNC应用程序接口规范第二版”。它是发那科为自家数控系统设计的一套基于以太网TCP/IP的通信协议库最早从FOCAS1发展而来现在已经成为发那科系统对外数据交换的事实标准。很多刚接触的人会把它类比成“发那科机床的网口Modbus”这个说法大体对但不够准确。FOCAS2的覆盖面比Modbus宽得多它不仅能读寄存器类数据还能操作文件、传输加工程序、读写PMC内部信号甚至远程执行部分系统指令。在发那科30i、0i、16i、18i、21i等主流系列上只要开了相应功能选项就能通过这套接口拿到很完整的数据。从开发角度看focas2开发包本质是一组动态链接库Windows下是DLL、头文件和示例工程。你用C、C、C#、VB.NET都能开发核心逻辑就是上位机作为TCP客户端主动连上CNC的8193端口然后通过一组以cnc_开头的API函数完成数据读写。1.2 这套开发包实际能做什么我见过不少项目对FOCAS2的认知停留在“读个坐标、看个开机状态”上。实际上它的能力边界要大得多。给你列一下我在现场项目里真实用到的场景实时状态采集各轴绝对坐标、机械坐标、相对坐标、剩余移动量主轴转速、进给倍率、切削负载。报警与日志读取当前报警、历史报警包括报警号、报警文本和发生时间。程序管理查询程序列表上传/下载加工程序甚至远程下发程序并启动运行。参数读写读写CNC参数、宏变量在线调整刀具补偿、坐标系偏移。PMC信号交互读写PMC的内部继电器、数据表用于和外部PLC做信号握手。这几个能力基本覆盖了数字化车间对机床侧八成以上的数据需求。所以很多做MES、SCADA、刀具管理系统、设备远程运维平台的公司在对接发那科设备时清一色选FOCAS2不是没有原因的。相比走OPC UA中间网关原生协议拿到的数据最全也最稳定相比让机床侧开Modbus又不用额外占用机床内部的通信资源属于典型的“一次配置长期受益”。2. 环境准备开发包安装与通信参数配置FOCAS2开发包不是网上随便下个盗版就能用的正规渠道是找发那科代理商或机床厂商要随设备订单或者售后支持一起提供。拿到手之后别急着写代码先花半小时确认几件事能省掉后面一大半的折腾时间。2.1 拿到开发包后先核对四样东西正常情况下开发包解压后包含这么几类内容DLL库文件、头文件.h、示例工程和一本PDF格式的函数说明书。我会建议你重点核对四样东西DLL的位数版本。老版本常见的是fwlib32.dll对应32位应用后来发那科也提供了fwlib64.dll对应64位应用。注意这里的“32/64”要和你的上位机程序编译目标一致32位程序配32位DLL64位程序配64位DLL混用必然出问题。头文件版本。头文件里定义了结构体、常量和函数声明版本太老会缺新系统才有的函数和结构体字段。比如做30i-B、0i-F这些新型号项目时老版本的fwlib32.h里可能都没有对应的结构体定义。示例工程的语言。官方一般给的是C语言示例部分渠道会附C#版Demo。如果你用C#或VB.NET参考C语言示例时要自己翻译DllImport声明这块工作量不小。说明书里的“支持系统”清单。你要对接的机床系统型号是否在这个清单上以及需要打开哪个功能选项说明书里都写得很清楚。提示FOCAS2没有离线模拟环境DLL连不存在的IP只会反复超时。开发阶段最好准备一台实际机床或者用现场测试机先调通通信链路否则代码写完了也没法验证。2.2 CNC侧和PC侧的通信配置通信配置是整个项目里最关键的一步配置错了后面全白搭。我按0i-F系列的操作习惯给你一个通用流程老系统0i-D、16i/18i/21i菜单位置略有不同但逻辑一致。CNC侧操作步骤在MDI或编辑方式下按下SYSTEM键进入系统菜单。找到“内嵌以太网设定”或“以太网设定”选项进入。给CNC设一个固定IP比如192.168.1.1子网掩码255.255.255.0。确认通信端口是8193这个端口是FOCAS2的固定端口一般不用改。确认FOCAS2功能选项已经开通。有的系统在“诊断”页面里有对应参数位如果选项没开上位机怎么连都连不上或者连接后读不到数据。保存设置重启CNC使网络参数生效。PC侧操作步骤把电脑网口设成和CNC同网段的静态IP比如192.168.1.100子网掩码255.255.255.0。拿网线直连电脑和CNC的以太网口或者通过工业交换机连也行。先打开命令行ping 192.168.1.1确认网络通。用TCP工具比如常见的Socket工具主动连接192.168.1.1:8193能连上说明基本链路OK。这一步做完你再开始写代码心里就有底了。很多新手一上来就写程序结果连接超时折腾半天发现是IP没配对或者防火墙拦着浪费时间不说还容易把问题搞混。3. 核心函数拆解连接、读坐标、读状态FOCAS2的函数很多但你不能贪多求全。实际项目里高频使用的其实就那么十来个。你把这几个函数的签名、参数含义、返回值判断吃透就能解决大部分需求。3.1 建立连接cnc_allclibhndl3必知细节建立连接是FOCAS2所有操作的入口函数名是cnc_allclibhndl3。这个“allclib”可以理解为“建立所有库句柄”是一次性初始化整个通信环境的意思。它与早期版本的区别在于新版函数把IP、端口、超时时间都整合在一个调用里用起来更简单。函数的典型签名长这样short cnc_allclibhndl3(const char *ipaddress, unsigned short port, int timeout, unsigned short *FlibHndl);ipaddressCNC的IP地址传字符串比如192.168.1.1。port端口号固定填8193。timeout连接超时时间单位毫秒一般设3000~5000太短容易误报失败太长出错时等待久。FlibHndl输出参数成功后返回一个句柄值后续所有操作都要拿这个句柄当“通行证”。如果返回值是0表示连接成功接下来就可以用返回的句柄调用读数据函数了。我建议你把这个连接函数封装成一个公共方法统一管理超时参数和错误日志不要每次调用都重新写一遍。3.2 读取坐标ODB_POS结构体踩坑记录连接建立后第一个要读的往往是坐标。FOCAS2读坐标的函数是cnc_rdposition它会把坐标数据填到一个叫ODB_POS的结构体里。这个结构体是典型的“看起来简单用起来坑多”。我摘一段C#里常用的结构体定义对照着C头文件翻译的[StructLayout(LayoutKind.Sequential, Pack 1)] public struct ODB_POS { public short dummy; public short type; public short prg_no; public ushort prg_m; public short seq_no; public ushort seq_m; [MarshalAs(UnmanagedType.ByValArray, SizeConst 12)] public double[] abs_pos; [MarshalAs(UnmanagedType.ByValArray, SizeConst 12)] public double[] mach_pos; [MarshalAs(UnmanagedType.ByValArray, SizeConst 12)] public double[] rel_pos; [MarshalAs(UnmanagedType.ByValArray, SizeConst 12)] public double[] dist_left; }这里有几个特别容易踩的坑结构体里的轴数组长度头文件里用的是MAX_AXIS但不同系统轴数不一样有的机床是3轴有的是5轴联动。你数组长度定义小了数据会越界定义大了比如12程序也能跑但要按实际轴数去取有效数据。Pack 1和默认对齐方式有区别。如果直接用C#默认的LayoutKind.Sequential不指定Pack结构体内部会按8字节对齐导致字段偏移和C头文件不一致读出来的坐标就是乱的。我见过不止一个项目问题出在这个对齐参数上。double数组在C#里需要MarshalAs(UnmanagedType.ByValArray)显式标记长度不然没法正常转换。读取时type参数传-1表示读取所有坐标类型也可以传1绝对坐标、2机械坐标、3相对坐标、4剩余移动量单独取一种。多数项目做数据展示用-1一次拿全然后从abs_pos、mach_pos这些数组里对应取0号、1号、2号轴的数据即可。3.3 常用功能函数速查与返回值判断连接和读坐标搞定之后剩下的功能函数基本都是同样的套路传句柄、传参数、拿结构体、判断返回值。我挑几个使用频率最高的列个速查表函数作用关键参数提示cnc_rdalm / cnc_rdalm2读取报警输出参数含报警号、报警文本cnc_rdprogram读取当前程序信息返回程序号和程序名cnc_rdparam读取CNC参数按参数号和轴号读取cnc_rdmacro / cnc_wrmacro读写宏变量注意写宏变量要有对应权限cnc_rdpmc / cnc_wrpmc读写PMC地址地址类型和数据长度要明确cnc_upload / cnc_download程序文件传输需确认文件传输方向和许可cnc_gettime读取CNC系统时间用于同步时间戳cnc_freelibhndl断开连接程序结束时必须调用你还会发现一个规律所有函数返回值都用short类型0表示成功其他数字是各种错误码。常见错误码含义我在第6章的排查表里详细说。这里提个醒写代码时千万不要只判断“不等于0就是错”尽量把错误码映射成可读的日志文本否则现场排查时会很痛苦。4. 实战用C#写一个CNC状态采集程序理论说再多不如一段能跑的代码。这一节我用C#给你演示一个最小可用的采集程序功能包括连接CNC、循环读取坐标、异常断开后自动重连。你把它跑通再往里面加报警读取、程序管理就很容易了。4.1 工程配置与DLL引用的三个坑在写代码之前先解决工程配置的问题。C#调用FOCAS2属于典型的P/Invoke平台调用有几个坑必须提前避开坑一项目目标平台要和DLL位数一致。如果你用的是fwlib32.dll项目生成时要选“x86”或“AnyCPU”并取消“首选32位”如果项目配置成了x64又只引用了32位DLL运行时会直接报“试图加载格式不正确的程序”。反过来同理。坑二DLL文件要能找得到。最简单的办法是把DLL复制到exe输出目录或者放到System32目录。更规范的做法是在工程里加一个lib文件夹把DLL包含进来并在属性里设置“如果较新则复制”。坑三DllImport的EntryPoint必须和头文件里的导出函数名完全一致。FOCAS2的导出函数名是区分大小写的C声明无修饰不要自己改名字也不要手滑写成别的。注意不要在界面上频繁调用连接/断开函数。现场经验是短时间反复重连会让CNC侧的通信模块出现过载保护轻则连接失败重则要重启系统。连接成功后就保持长连接断开操作放到程序退出和异常恢复场景里。4.2 示例代码连接、采集、断线重连下面这段代码是基于我之前做过的某数控车间数据采集项目简化出来的只保留了核心逻辑。你根据自己的项目调整IP和采集周期即可。using System; using System.Runtime.InteropServices; using System.Threading; namespace Focas2Demo { public class FocasCollector { // 引入DLL函数 [DllImport(fwlib32.dll, EntryPoint cnc_allclibhndl3, CallingConvention CallingConvention.Cdecl)] public static extern short cnc_allclibhndl3(string ipaddress, ushort port, int timeout, out IntPtr handle); [DllImport(fwlib32.dll, EntryPoint cnc_rdposition, CallingConvention CallingConvention.Cdecl)] public static extern short cnc_rdposition(IntPtr handle, int type, out ODB_POS pos); [DllImport(fwlib32.dll, EntryPoint cnc_freelibhndl, CallingConvention CallingConvention.Cdecl)] public static extern short cnc_freelibhndl(IntPtr handle); // ODB_POS结构体定义 [StructLayout(LayoutKind.Sequential, Pack 1)] public struct ODB_POS { public short dummy; public short type; public short prg_no; public ushort prg_m; public short seq_no; public ushort seq_m; [MarshalAs(UnmanagedType.ByValArray, SizeConst 12)] public double[] abs_pos; [MarshalAs(UnmanagedType.ByValArray, SizeConst 12)] public double[] mach_pos; [MarshalAs(UnmanagedType.ByValArray, SizeConst 12)] public double[] rel_pos; [MarshalAs(UnmanagedType.ByValArray, SizeConst 12)] public double[] dist_left; } private IntPtr _handle IntPtr.Zero; private readonly string _ip; private readonly ushort _port 8193; private readonly int _timeout 5000; public FocasCollector(string ip) { _ip ip; } public bool Connect() { short ret cnc_allclibhndl3(_ip, _port, _timeout, out _handle); if (ret 0) { Console.WriteLine($[{DateTime.Now:HH:mm:ss}] 连接CNC成功句柄{_handle}); return true; } Console.WriteLine($[{DateTime.Now:HH:mm:ss}] 连接CNC失败错误码{ret}); return false; } public void Disconnect() { if (_handle ! IntPtr.Zero) { cnc_freelibhndl(_handle); _handle IntPtr.Zero; Console.WriteLine($[{DateTime.Now:HH:mm:ss}] 已断开连接); } } public void CollectLoop(int intervalMs) { int failCount 0; while (true) { if (_handle IntPtr.Zero !Connect()) break; ODB_POS pos; short ret cnc_rdposition(_handle, -1, out pos); if (ret 0) { failCount 0; Console.WriteLine($X{pos.abs_pos[0]:0.000} Y{pos.abs_pos[1]:0.000} Z{pos.abs_pos[2]:0.000}); } else { failCount; Console.WriteLine($读取坐标失败错误码{ret}); if (failCount 3) { Console.WriteLine(连续失败3次准备断开重连...); Disconnect(); Thread.Sleep(2000); Connect(); failCount 0; } } Thread.Sleep(intervalMs); } } } class Program { static void Main(string[] args) { var collector new FocasCollector(192.168.1.1); if (collector.Connect()) { collector.CollectLoop(1000); } collector.Disconnect(); } } }这段代码里有一个细节值得展开说采集循环里的“连续失败3次才重连”。我一开始做的时候每失败一次就立刻重连结果在CNC偶尔因内部处理繁忙返回错误码时会把通信链路搞崩。后来改成“连续失败多次才重连”故障期间只告警不重连稳定性提升明显。采集周期我一般设1秒如果对实时性要求高最短可以压到500ms但再低就要小心CNC侧和上位机两侧的性能开销了。5. 从FOCAS2到URF R330 SDK工业设备通信的通用套路写到这里顺便聊聊最近热起来的URF R330 SDK开发包。虽然URF R330是工业机器人控制器和FOCAS2对接的数控系统不是一类设备但我实际看过它的SDK之后发现工业设备上位机通信的开发方法论惊人地一致。把这个话题放在一起讲是想帮大家跳出“只会用FOCAS2”的思维定势。5.1 URF R330 SDK开发包带来的启发URF R330的SDK开发包结构上跟focas2开发包像是一个模子刻出来的同样是动态库加头文件加示例工程的组合同样通过以太网与控制器通信同样把数据封装成结构体同样用错误码区分失败原因。我拿到手的第一反应是这类SDK的核心接口基本跑不出这几个分类设备发现与连接管理、状态信息读取坐标、速度、IO、程序信息、指令下发运动、程序启停、IO操作、文件管理程序上传下载。你在FOCAS2里练熟的能力换到URF R330上一样能用只是函数名和结构体字段换了而已。所以我一直觉得与其说你要“学会FOCAS2”不如说你要“学会一类工业设备的SDK开发方法”。FOCAS2是你练手的好载体——因为它资料齐全、应用面广、踩坑记录丰富而一旦你把它吃透以后再接触其他厂家的设备SDK上手速度会快很多。5.2 工业设备SDK开发的五个通用经验结合FOCAS2和URF R330 SDK这两类开发包的实战经历我总结了五个通用的经验希望能帮大家少走弯路。第一先跑官方Demo再读文档。不管文档写得多详细都不如先把Demo跑起来看到真实数据来得直观。我给新人的标准流程是配环境、跑Demo、确认能读到数据然后再回头啃文档里的细节。这样前面提到的结构体对齐、参数含义问题会在跑通Demo的过程中自然暴露出来。第二连接管理是稳定性的生命线。连接成功后要保持长连接不要在业务逻辑里频繁connect/disconnect连接失败要有退避重试机制不要硬怼。这些问题在现场最容易出事故尤其是机床这类24小时运行的设备通信不稳定比数据慢更致命。第三结构体定义要严格按头文件来。不管是C#、VB.NET还是Python跨语言调用动态库时结构体的内存布局、对齐方式、数组长度都要和C头文件保持一致。你可以在代码里写个调试函数把结构体每个字段的偏移量打印出来和头文件里手动算的偏移量对比这是排查乱码、错位最有效的手段。第四错误码必须落在日志里。FOCAS2、URF R330和大多数工业SDK一样用整数错误码区分失败原因。开发阶段如果只判断“成功/失败”到了现场遇到问题就两眼一抹黑。我习惯封装一个GetErrorText(code)函数把所有错误码都映射成可读文本并且在每次调用SDK函数后都记录日志。项目上线后靠日志反查问题能省一半时间。第五版本兼容性要提前确认。设备固件版本、SDK版本、系统软件选项这三者之间有关系。比如FOCAS2的DLL版本太老连新型号系统时可能连不上URF R330的SDK如果和控制器固件版本不匹配也有可能出现功能调用异常。项目启动前把这三方版本列成一个对照表测试环境先验证这步不能省。6. 常见问题与排查技巧实录FOCAS2项目里踩过的坑我挑几个高频的整理成表再讲一个真实的事故案例。这一节的信息是常规文档里不会写的但对排查现场问题特别有用。6.1 高频问题速查表现象可能原因排查方法连接超时返回错误码-16IP没配对、端口不对、CNC侧选项没开先ping通用TCP工具连8193端口确认FOCAS2选项程序运行报“试图加载格式不正确的程序”DLL位数和进程位数不匹配确认是fwlib32还是fwlib64统一工程目标平台连接成功但读坐标返回-1句柄失效、结构体长度不够、参数类型错误检查句柄是否有效按头文件核对结构体定义读取的坐标值明显不对结构体对齐方式错误、数组索引取错确认Pack对齐方式按实际轴数取数据中文报警文本乱码DllImport没指定字符集或系统编码不一致用Unicode版本函数在DllImport中指定CharSet运行一段时间后连接断开网络抖动、采集频率太高、CNC侧空闲超时降低采集频率加断线重连机制用工业交换机多线程同时访问一个句柄崩溃FOCAS2句柄不是多线程安全的对同一句柄加锁或每个线程独立连接补充一个很容易被忽略的坑有些老机床的以太网模块在长时间无操作后会自动断开TCP连接。如果你的采集程序9点下班后没数据写入第二天早上发现连不上了多半是这个原因。解决思路不是抱怨而是在程序里加一个无操作保护机制——每隔30秒发一个轻量级的读取请求保持连接活跃或者做好重连逻辑。6.2 一个真实事故轴数不一致引发的读取失败之前做一个五轴加工中心的数据采集项目调试时一切正常但正式部署到另一台三轴机床后程序读坐标一直失败。当时我排查了很久网络通、连接成功、权限也有就是cnc_rdposition返回非零。后来逐字段对比两套系统后发现问题出在结构体轴数组长度上。我在C#里定死了数组长度为5结果三轴机床的轴配置里返回的轴信息方式不一样结构体内部的偏移被系统按它实际的轴数计算了两边对不上数据自然读不出来。后来我按头文件把数组长度统一放大到MAX_AXIS12再按实际机床轴数去取有效数据问题就解决了。这个案例的教训是写结构体时永远不要按“当前这台机床”去定死长度。CNC系统的轴数、通道数会随设备配置变化结构体定义要按SDK头文件的最大限度来取数据时再根据设备实际能力做判断。我当时如果早点把所有字段的长度都按最大值定义可能调试一个小时就搞定了。这也是我为什么在4.2的示例代码里把数组SizeConst直接写成12的原因。另外再分享一个经验现场排查问题时最快的路径不是看代码而是先把通信链路分成四段定位——网络层能不能通、TCP握手能不能成功、SDK连接函数返回什么、具体功能函数返回什么。每段都打日志很快就能锁定问题在哪一层。FOCAS2这类SDK的报错码虽然多但只要链路分段清晰大部分问题半小时内都能定位到根因。本文还有配套的精品资源点击获取