ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于C#的Fanuc数控系统上位机开发:Focas通信与车间设备联网实践

基于C#的Fanuc数控系统上位机开发:Focas通信与车间设备联网实践 简介面向工业自动化与数控机床运维场景这是一套基于C#开发的FANUC系统上位机管理工具聚焦车削类机床的多设备数据采集、运行状态监控、故障报警与刀具寿命管理。工程覆盖FANUC FOCAS通信库调用fwlib32.cs、轴控制、PMC与NC程序交互等模块界面层包含参数、报警、刀具寿命管理等子模块可帮助C#开发者快速理解上位机与数控系统间的数据对接方式也能为设备管理人员提供集中监控多台机床的落地参考。压缩包共82个文件以28个C#源码为主辅以14个DLL程序集、资源与界面布局文件resx/resources并附带可执行程序、配置及数据库文件整体体积14.75MB目录结构完整便于直接研读或二次开发。已有182人学习下载适合需要搭建机床数据采集系统或从事工业上位机开发的工程师参考。1. Fanuc_4_7.zip先用 C# 上位机把车间设备从黑匣子里捞出来做车间项目的都知道Fanuc 系统的数据不拉出来产线就是黑匣子。操作工在面板上看到坐标、报警、倍率管理层在办公室看不到等设备停了、程序传错了只能跑到机床前面拿 U 盘拷一问三不知。Fanuc_4_7.zip 里封的就是一个 C# 上位机管理系统目标很直接通过以太网连上 Fanuc 数控系统做连接测试、坐标读取、报警采集、程序列表管理再把数据落到本地库。适合正在折腾设备联网、MES 对接、想自己写一套车间监控看板的工程师。这个包不是教学 Demo是一套能开机直接跑的 Windows 上位机工程Focas 调用、界面、配置、回调都齐了。我拿到之后第一件事不是编译而是先看它的通信封装。Fanuc 上位机开发看起来难本质就是两件事把 Focas 库调对把返回的数据接住。这篇就把我拆包时确认过的东西——文件结构、连接配置、主流程代码、踩过的坑——按可复现的顺序写出来。2. 拆包看结构文件分层、Focas 版本与 returnwfk 回调2.1 解压后先看什么一只眼看工程一只眼看 DLL解压 Fanuc_4_7.zip 之后第一件事不是双击 sln而是先把目录树的层次理清楚。这个包的布局是典型的上位机工程一个解决方案里挂着界面项目和通信库项目。通信库单独拆出来是好事说明作者给后面的扩展留了口子我后面改造断线重连、接 MES 时不用动界面代码。文件/目录主要作用FanucManage.sln解决方案入口打开后先确认目标框架fwlib32.dllFocas 核心库所有 CNC 通信都走它App.configIP、端口、超时时间、日志路径的集中配置通信层项目封装 Focas 调用暴露连接、读取、回调方法界面项目WinForms 的监控窗体、程序管理窗体数据目录SQLite 数据库文件和日报模板拿到手先做两步第一步CtrlShiftB编译一遍看缺不缺引用第二步打开 App.config 看连接参数是不是还被默认值占着。很多下载下来的工程编译失败都是因为里的 DLL 引用路径是绝对路径换机器就断了。删掉引用重新添加一次指向本地 fwlib32.dll比去改 csproj 里的绝对路径快得多。配套的连接配置一般长这样appSettings add keyFanucIp value192.168.1.50/ add keyFanucPort value8193/ add keyFocasTimeout value10/ add keyPollInterval value1000/ /appSettingsFanucIp是 CNC 面板上配好的机床 IPFanucPort默认 8193这是 Focas 以太网通信的标准端口如果机床侧改过这里要同步FocasTimeout是连接超时单位秒设太短在大网络环境里会误报离线设太长机床断网后界面会卡住不动PollInterval是轮询间隔单位毫秒。我习惯把这几个参数全部做成可配置而不是硬编码在代码里后面调试不同车间设备时不用重新编译。2.2 CNC 侧通信配置IP、端口与 Focas 库的位数这部分是新手最常翻车的地方。PC 端代码写得再对CNC 侧没有开嵌入式以太网功能Focas 一样连不进去。连接 Fanuc 前必须在机床面板上确认几项设置。设置项典型值说明IP 地址192.168.1.50与 PC 同一网段子网掩码255.255.255.0不要跨三层访问 CNC嵌入式以太网开启在系统参数里打开否则 TCP 端口不监听端口号8193Focas 标准端口可改但必须两边一致Fanuc 上位机通信有两种常见形态老设备走 HSSB 板卡PC 里插一块专用板卡用电缆连机床现在的主流是走以太网直接用网线连交换机。Fanuc_4_7.zip 里的代码走的是以太网方式也就是调用cnc_allclibhndl3传 IP 和端口。如果你要接的是带 HSSB 的老设备这段代码就要换板卡厂商提供的接口不能直接套用。Focas 库本身还分 32 位和 64 位这是比 IP 配置更隐蔽的坑。工程里如果引的是fwlib32.dll整个项目生成目标必须强制 x86哪怕你电脑是 64 位 Windows反过来引了fwlib64.dll才能用 AnyCPU 或 x64。否则编译能过运行到调用 Focas 函数时会直接崩在 DllImport 入口典型表现就是异常0xC0000005后面避坑章再展开。2.3 returnwfk资源里的数据回调在交接点什么第一次看到returnwfk这个命名我愣了一下这名字不像规范的函数命名。但放到工程里看它的作用其实非常清楚底层读完 CNC 数据之后把结果交给上层界面去刷新。它就是整个通信层与界面层之间的数据交接点本质是一个回调事件。public delegate void ReturnWfkHandler(object sender, DeviceDataEventArgs e); public class DeviceDataEventArgs : EventArgs { public string DeviceIp { get; set; } public double[] AxisPos { get; set; } public string CurrentProgram { get; set; } public int AlarmCount { get; set; } } public event ReturnWfkHandler ReturnWfk; private void RaiseDataReceived(DeviceData data) { ReturnWfk?.Invoke(this, new DeviceDataEventArgs { DeviceIp _ip, AxisPos data.AxisPos, CurrentProgram data.CurrentProgram, AlarmCount data.AlarmCount }); }这段代码的价值在于把通信线程和 UI 线程彻底解耦。底层 Focas 读取是在后台线程跑的读完之后不做任何界面操作只抛一个事件界面层自己去订阅这个事件再用Invoke或async/await切回 UI 线程刷新表格。我看过太多 Fanuc 上位机源码直接把 Windows 控件丢在线程里更新跑起来界面假死这个包至少在结构上没踩这个错。DeviceDataEventArgs里的字段就是一个设备状态快照IP 用来区分多台机床AxisPos存各轴坐标AlarmCount给红色报警灯用CurrentProgram用来显示当前运行的 O 号程序。真要在生产环境跑我会在这个类型里再加主轴转速、进给倍率、当前模式这几个字段后面接 MES 时省很多事。3. 把连接主流程跑通连接测试、坐标读取与报警解析3.1 连接测试从 cnc_allclibhndl3 开始参数与返回值Fanuc 上位机开发最核心的入口函数就是这个cnc_allclibhndl3它的作用是通过以太网与 CNC 建立通信句柄。几乎所有的 Focas 功能都依赖这个句柄读坐标、读报警、传程序第一步都是它。资源里对它的封装非常标准我直接按生产标准重新贴一遍[DllImport(fwlib32.dll, CharSet CharSet.Ansi)] private static extern short cnc_allclibhndl3( string ipaddr, ushort port, short timeout, out ushort handle); ushort hndl; short ret cnc_allclibhndl3(192.168.1.50, 8193, 10, out hndl); if (ret 0) { // 连接成功句柄保存在 hndl 中 } else { // ret 非 0对照 fwlib 错误码表排查 }ipaddr是 CNC 的 IP 字符串port是 Focas 端口标准是 8193timeout是建立连接超时单位秒这里给 10 是保守值我实际调试时先用 3 秒快速验证网络通不通通了再改成 10 进正式循环handle是输出参数连接成功后保存句柄后续所有读取操作都要带上它。返回值是short0 表示成功非 0 值需要去 fwlib 手册的错误码表里查常见的有连不上、参数错误、机床侧未就绪几类。连接成功之后不要急着读数据先用一句话日志把版本信息打出来。Focas 里可以用cnc_rdversion读系统版本能读出来说明句柄有效、权限开放、DNS 和端口都是通的。这个动作我养成了习惯每次现场调试第一件事就是看版本号比直接读坐标更能定位问题层次。3.2 坐标与倍率读取ODBPOS 结构体与轴循环句柄建立之后坐标读取就是最基础也最常用的功能。Fanuc 的 Focas API 里坐标不是一次性返回一个数组而是通过cnc_rdposition一个轴一个轴地读轴号从 1 开始计数。返回的数据放在ODBPOS结构体里C# 里需要手动声明结构布局[StructLayout(LayoutKind.Sequential, Pack 4)] public struct ODBPOS { public short dummy; public double data; } public static extern short cnc_rdposition( ushort hndl, short type, short length, out ODBPOS pos); double[] axes new double[4]; for (int axis 1; axis 4; axis) { short ret cnc_rdposition(hndl, 1, (short)axis, out ODBPOS pos); if (ret 0) { axes[axis - 1] pos.data; } }type是坐标类型的开关1 是绝对坐标2 是相对坐标3 是机械坐标。我一般读绝对坐标因为界面显示给操作工看的和面板上默认显示一致做位置监控用机械坐标更稳定它不受 G54 工件坐标系漂移影响。length这里传的是轴序号1 代表 X 轴2 代表 Y 轴3 代表 Z 轴4 代表第四轴。资源里用了一个 4 轴的循环来覆盖常见加工中心如果是车床只要两个轴循环改成 2 就行。倍率读取我通常不走位置接口而是用cnc_rdspdl读主轴转速和实际进给再用 Focas 的倍率接口读进给倍率百分比。倍率数据对判断设备是否在“干等”很有用比如程序在跑但倍率打到 0设备实际上是在空转。这个字段往 MES 传的时候一定要带单位Focas 返回的进给率有时是 mm/min 有时是 mm/rev看机床是铣削还是车削模式。3.3 报警与运行状态解析Focas 返回的报警号怎么用报警读取用的是cnc_rdalarm2它返回的是报警条数和报警号列表。这里有个新手容易误判的地方Focas 返回的是报警代码数字不是报警文本。想要报警文本要么自己维护一张报警号对照表要么去 Fanuc 系统报警手册里查。上位机里常见的做法是把报警号原样存库后续由 MES 平台统一映射。[StructLayout(LayoutKind.Sequential, Pack 4)] public struct ODBALARM2 { public short data; public short num; } public static extern short cnc_rdalarm2( ushort hndl, out ODBALARM2 alarm); short ret cnc_rdalarm2(hndl, out ODBALARM2 alarm); if (ret 0 alarm.num 0) { // alarm.num 表示当前报警数量 }ODBALARM2结构体里data是报警类别num是当前报警条数。完整报警明细字段在 Focas 头文件里是一个结构数组每个报警项包含报警类型、轴号、报警编号。实际项目里我一般不会把报警明细全部展开到界面只保留报警计数配合一个“最新一条报警号”让看板能弹出一条醒目提示就行。运行状态读取也有自己的专属接口Focas 里用cnc_statinfo拿设备当前模式返回的是自动、手动、MDI、JOG 这类状态。这个字段要和坐标、报警放在同一个DeviceData快照里因为在轮询循环里一次连接多次调用状态会互相不一致。先收集完所有字段再一起抛returnwfk回调这是保证数据一致性的关键细节。4. 从读到管程序管理、多机轮询与 SQLite 落库4.1 程序清单与上下载FTP 旁路和 Focas 文件接口怎么选程序管理是 Fanuc 上位机里“含金量”最高的模块。先读取机床里的程序列表这个用cnc_rdprgnum就可以拿总数和主程序号public static extern short cnc_rdprgnum( ushort hndl, out int allNum, out int mainNum, out int pgmNum); short ret cnc_rdprgnum(hndl, out int allNum, out int mainNum, out int pgmNum); if (ret 0) { Console.WriteLine($全部程序数: {allNum}, 主程序数: {mainNum}); }allNum是存储区内全部程序数量mainNum是主程序数量pgmNum通常用于维护例行程序。这个接口的价值在于做“设备里有程序但不知道是谁传进去的”这类审计需求配合数据库里记录的上次下载记录能及时发现产线程序被非法改动。程序上下载我建议分两条路走。路径一是 Focas 的文件接口调用cnc_open、cnc_read、cnc_close组合代码可以直接嵌在工程里适合程序量小、不需要人工介入的场景。路径二是 FTP 旁路直接在 CNC 侧开启 FTP 服务器功能PC 端用标准 FTP 客户端上传下载更适合大程序、批量传输的场景。资源里给的是 Focas 文件接口的方案我实际接产线时更倾向 FTP原因是 Focas 大数据传输时抗中断能力弱网络抖动一次就容易半途失败FTP 有断点续传的兜底。对比项Focas 文件接口FTP 旁路传输稳定性一般较好依赖端口819320/21大程序支持较弱强代码量少中人工干预无需需要额外维护 FTP 账号4.2 多机轮询不卡界面后台线程和 UI 脱钩的三行写法生产车间很少只有一台 Fanuc一般都是一排加工中心。轮流读取多台设备的坐标和状态如果放在 UI 线程的 Timer 里做读第一台的时候界面就卡住了。解决办法是轮询动作放进后台线程只把结果抛回 UI。资源里的returnwfk回调正好配合这个场景我一般写成这样private async Task PollDeviceAsync(string deviceIp) { while (!_cancelled) { DeviceData data await Task.Run(() ReadDeviceOnce(deviceIp)); RaiseDataReceived(data); await Task.Delay(_pollInterval); } }ReadDeviceOnce里是 Focas 的连接和读取调用这整个方法在线程池上执行不碰 UI 控件RaiseDataReceived把数据抛回调界面订阅者拿到数据后用BeginInvoke刷新表格。_pollInterval就是前面配置里的轮询间隔我一般设在 1000 到 3000 毫秒之间太短会频繁占 Focas 句柄太长报警不及时。多机并发时要注意每台设备各持有一个独立的hndl句柄不要共享。Focas 的句柄是设备级的两个线程共用一个句柄会出现数据串台的诡异现象A 机床的坐标刷新到 B 机床的行上。我在一个项目里就被这个坑过排查到最后才发现是句柄被静态变量共享了。4.3 数据落库与日报SQLite 表结构先定好上位机没有数据库等于白做。坐标读出来只在界面上闪一下设备报警了没有历史记录后面追溯完全没法做。这个包的数据层用的是 SQLite单机部署不用装数据库服务文件拷贝就能带走非常适合车间工控机。CREATE TABLE IF NOT EXISTS device_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_ip TEXT NOT NULL, record_time TEXT NOT NULL, axis_x REAL, axis_y REAL, axis_z REAL, current_program TEXT, alarm_count INTEGER, feed_rate REAL );record_time用 ISO 格式的字符串存储方便跨平台比对device_ip作为设备标识多台机床用一个库时靠它区分axis_x/y/z用 REAL 够用坐标精度到小数点后三位足够alarm_count每次轮询都写写 0 也没关系后面做日报时可以直接对非 0 记录做统计。写入频率高的话要给record_time加个索引不然三个月之后查询会明显变慢。落库的代码不用写得多花哨重点是把写入动作也放到后台线程。我在资源里看到它直接用 ADO.NET 插入做单台设备没问题多台设备并发轮询时我还是建议用一个简单的写队列ConcurrentQueue攒一批批量插入减少磁盘 IO 压力。日报报表就从这张表里做聚合查询按device_ip分组统计每台设备的运行时间、报警次数、程序切换次数。5. 避坑排查连接、字节对齐与 C# 调 C DLL 的五个现场5.1 现象ping 得通Focas 就是连不上CNC 的 IP 在 PC 上 ping 完全通但cnc_allclibhndl3一直返回非 0。原因Fanuc 数控系统的嵌入式以太网功能没有开启TCP 8193 端口没有监听或者 Windows 防火墙拦了 fwlib32.dll 的出站连接。解决先到机床面板的系统参数里确认嵌入式以太网处于开启状态再检查端口号是否两边一致。然后在 PC 防火墙上放行 fwlib32.dll或者直接把工控机加入域内信任网络。我现场调试时还有一个玄学步骤把 PC 的网卡关闭 IPv6只留 IPv4有些老版本 Focas 在双栈环境下会选错网络路径。5.2 现象坐标读出来全是 0 或者数值错乱连接正常版本号能读到但cnc_rdposition返回的data永远是 0或者数值和面板显示差好几个数量级。原因结构体布局不对。Focas 的底层是 C 语言结构体C# 声明时如果少了Pack或者默认对齐方式不对读出来的字段位置就偏移了。坐标全 0 还可能是把机械坐标和绝对坐标搞混机械坐标在某些配置下是相对机床零点的看起来和面板上显示的工件坐标完全不同。解决结构体上必须标注[StructLayout(LayoutKind.Sequential, Pack 4)]在 x64 环境下有时要改成Pack 8具体要看 DLL 版本。坐标类型先固定用绝对坐标type1做验证和面板逐轴对比一致后再扩展其它类型。5.3 现象调用 DLL 崩在 0xC0000005位置不定程序跑着跑着突然抛出 AccessViolation或者直接进程崩溃错误码是0xC0000005。这个问题在 C# 调 C 类 DLL 时极其典型。原因DllImport 的函数签名与 DLL 实际导出不一致最常见的是short和ushort混用、字符串编码没指定CharSet.Ansi。另外一个高发原因是引了 32 位 fwlib32.dll但项目编译成了 x64运行时数据栈根本对不上。解决先把项目生成目标固定成 x86因为绝大多数 Fanuc 附带的 Focas 库都是 32 位的再核对每个 DllImport 的参数类型字符串参数必须追加CharSet CharSet.Ansi。每次换 Focas 版本后我都强制走一遍连接、读坐标、读报警三个用例一个不对就回查签名。5.4 现象报警号读出来了报警文本永远为空cnc_rdalarm2返回正常num也大于 0但界面上死活显示不出报警具体内容。原因Focas 本身就只返回报警编号和类别不返回文本描述。上层需要自己维护一张报警号对应表。解决建一张报警映射表把常见报警号 1000、2000、3000 系列对应到中文描述查不到的报警号统一显示“未知报警请联系设备厂商”。报警表挂在数据库里不要写在代码里后续补充新报警不用重新编译上位机。5.5 现象界面一卡车间电话马上打过来上位机运行 20 分钟后窗体无响应必须关掉重开。原因轮询线程直接修改了 UI 控件或者轮询间隔小于 Focas 单次读取耗时后台线程越积越多最终把界面的消息队列堵死。解决所有 UI 更新全部走BeginInvoke绝不在后台线程碰控件轮询间隔最低设 1 秒如果单台设备完整读取超过 500 毫秒说明网络延迟大间隔要相应加大到 2 秒以上。我还加了一个看门狗统计如果连续 5 次读取耗时超过间隔值就自动把轮询间隔翻倍等网络恢复后再逐步降下来。6. 再往前走一步断线重连、MES 对接与宏变量自检6.1 断线重连把 cnc_freelibhndl 和重连逻辑串起来生产环境里网线松动、交换机重启、机床断电都是常态。上位机不能因为一次断连就永远停在“离线”状态。我改造这个包时加了断线重连检测到读取失败后先cnc_freelibhndl释放旧句柄等 5 秒重新cnc_allclibhndl3连续失败 3 次就把状态切到离线并弹提示。6.2 数据推给 MESJSON 还是 OPC UA车间里既有 Fanuc 也有西门子时数据最后都会汇到一个 OPC 网关或 MES 平台。Fanuc_4_7.zip 的数据库落盘已经为 MES 留好了基础再往前走一步就是在returnwfk回调里增加一个分发通道同一份DeviceData一份写本地库一份序列化成 JSON 推给消息队列MES 那边订阅即用。6.3 宏变量自检用 #5021 验证通信没跑偏上真机之前我习惯做一次宏变量自检。Fanuc 的系统宏变量 #5021 是当前机械坐标 X 值用 Focas 的宏变量读取接口读出来和cnc_rdposition读到的机械坐标对比。两者一致说明 Focas 通信链路和数据类型转换都没问题不一致优先检查结构体Pack。从那次被坐标全 0 坑了半天的经历之后我每次接新机床都强制走一遍三步连接测试、坐标比对、报警清零。这套流程也推荐你直接拿去用。希望帮到你。本文还有配套的精品资源点击获取
返回列表