ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ERP-MES文件型接口设计:制造指图抽出与实绩计上落地要点

ERP-MES文件型接口设计:制造指图抽出与实绩计上落地要点

简介:《MES 接口设计说明书》面向SAP R/3与制造执行系统(MES)集成项目的开发、实施及接口设计人员,围绕MELEBUS-BMAS场景中的系统间接口集成提供设计参考。文档重点说明制造指图数据抽取、实绩数据计上、R/3与MES间排他控制等接口规格,并给出系统目的与系统化对象范围、适用前提条件、注意事项、想定业务流程图、接口文件一览与字段定义。压缩包内含1个doc文件,大小约178KB,篇幅紧凑,适合作为接口雏形程序(如ZMAF001、ZMAF005)及参数设定、文件交互逻辑的查阅材料。内容还覆盖接口文件仕样、程序仕样与参数设定等章节,便于按模块核对接口输入输出及处理逻辑。已有2668人学习,读者可借此梳理R/3-MES接口边界、文件交互方式与程序规格的组织方法,为实际项目中的接口设计、评审和开发验证提供参照。

1. 凌晨两点,指图一条没到:这套 MES 接口要解决什么

凌晨两点被电话叫起来,说是 MES 那边一整天没收到制造指图。登上 ERP 一看,作业日志里明明写着“抽出完成”,共享目录里的文件也在,时间戳停在八小时前。再翻一圈,发现问题出在ctrl/ORD.LCK这个管理文件上:上一轮作业被人中途用kill -9掐掉,锁文件没有清掉,后面每一轮启动都判定“对方处理中”,直接退出,一整天的指图全卡在门口。

这份《MES 接口设计说明书》拆的就是这个场景里的东西:一套运行在 ERP 与 MES 之间的文件型接口附加程序,ERP 侧负责抽制造指图和计实绩,MES 侧通过共享目录读写文本文件,中间靠管理文件做排他。文档覆盖系统规格、文件格式、程序结构和参数配置四块,程序部分还给了ZMAF001(指图抽出)和ZMAF005(实绩计上)两个实现的拆解。适合两类人:正在做 ERP-MES 对接、需要一份能照着改的接口骨架的开发者;以及已经上线、天天被重发和重复计上折腾的运维。里面关于唯一键、状态回写顺序和管理文件的那几条约定,基本都是实际调试中踩出来的经验。

2. 接口全景:两份文件、两条处理链路和一把管理文件锁

2.1 抽出与计上:两个方向,两条链路

整套接口只有两个业务动作,方向相反。ERP 到 MES 是制造指图抽出:把指图头信息和构成品目分别抽出来,写成两份接口文件,MES 侧读走之后组织生产。MES 到 ERP 是实绩数据计上:MES 按指图汇总投入实绩和完成实绩,写成两份文件,ERP 读进来登记成作业确认。

为什么要拆成头、明细两组文件而不是合并成一份。头信息一条指图一行,明细可能几十上百行,混在一起读的时候得靠行标识区分记录类型,一旦某行截断,后面全部错位。拆开之后结构单一,解析逻辑各写各的,出错时定位也快。常见的做法是两组文件共用同一个指图号做关联键,MES 侧先读头缓存,再按指图号循环挂明细。

处理方式是批处理,一天 1 到 2 次。这个频率决定了整套设计可以容忍几小时的延迟,但绝不容忍数据错位。实时性要求高的场景(比如按件报工、节拍采集)不适合套这个骨架,需要走消息通道。

MES 侧的功能不在这个附加程序里。说明书里对 MES 的描述都建立在“假定 MES 具备这些能力”的前提下,具体怎么实现是 MES 厂商的事。这一条在二次开发时特别容易忽略:拿到这份文档,等于拿到了 ERP 侧的接口和文件格式约定,不等于拿到了一个能跑通的完整系统。

2.2 共享目录里的文件布局

原文里有一条硬约束:接口文件通过文件共享访问,ERP 侧接口和 MES 侧接口两个处理必须能访问到同一个文件。这条约束是整套设计的地基,也是最容易出问题的地方,后面单独讲。

物理路径不写进代码,走逻辑路径加变式配置。换行代码(CRLF / LF)、日志保留期限也都在变式里。好处是同一套程序在 Windows 和 Unix 上都不用改代码,只要改配置。实际工程里我一般按下面这个结构落地:

# 共享挂载点下的目录约定(逻辑路径在变式里配,这里只是物理示例) /interface ├── out/ # ERP 写,MES 读 │ ├── ORDER_HEAD.txt # 制造指图头信息 │ └── ORDER_CMPT.txt # 指图构成品目 ├── in/ # MES 写,ERP 读 │ ├── CONFIRM_ISSUE.txt # 投入实绩 │ └── CONFIRM_DONE.txt # 完成实绩 ├── log/ # 开始结束日志 + 明细日志,按日期滚动 │ ├── ZMAF001_20240115_START.log │ └── ZMAF001_20240115_DETAIL.log └── ctrl/ # 管理文件,也就是"锁" ├── ORD.LCK # 指图处理锁 └── CNF.LCK # 实绩处理锁

ctrl目录必须和接口文件在同一个挂载点上,不能放在各自本地。曾经见过把锁文件放在 ERP 应用服务器本地盘、接口文件放在共享盘上的做法,相当于两个系统各锁各的,一次都没拦住重复处理。

日志分两级。开始结束日志记录每次作业的起止时间和总体结果,明细日志按记录逐条写处理内容。排查时先看开始结束日志定位到时间点,再翻明细日志找具体是哪条指图、哪个字段出的问题,比对着一个几十兆的大文件瞎找快得多。

2.3 适用范围与前提条件

这套接口不是通用的,原文列了一串前提条件,我整理成表,方便判断自己的场景能不能套:

约束项原文要求对落地的实际影响
工厂范围单工厂内 ERP 与 MES 对接多工厂要么每个工厂一套,要么在文件里加工厂字段自行扩展
生产方式只覆盖会发行制造指图的生产方式无指图的连续生产、流程行业套不上
处理频率批处理,1~2 次/天需要分钟级响应的场景不适用
服务器平台ERP 与 MES 侧均限定 Windows / Unix其他平台的文件 API 行为要重新验证
网络环境双方必须能访问同一份接口文件跨网段、权限、挂载点必须先打通
文件形式Windows 或 UNIX 格式文本文件换行代码由变式指定,不靠程序猜
编码处理接口不做代码转换,按透传处理编码必须由两端事先约定,程序不管
指图结构一个指图对应一个计划订单,可含多个作业,不支持副作业有副作业、联产品的要另行设计

“不做编码转换、原样透传”这一条值得单独说。这是把复杂度从程序转移到了约定上:程序简单了,但两端一旦编码不一致,症状就是一堆看不懂的乱码,而且不会报错,只是数据错了。我在项目上一般会强制两端都用同一种编码,并在文件头留一行标识,读的时候先校验这一行,对不上直接拒绝处理,比处理到一半才发现要省事。

2.4 管理文件怎么做锁

排他控制的规则朴素到有点简陋:处理中就在ctrl目录下创建一个管理文件,处理结束删掉;启动时先看管理文件在不在,在就说明对方正在处理,本次直接退出。

FORM frm_lock USING p_lockfile TYPE string CHANGING p_rc TYPE i. " 返回码:0=拿到锁 1=对方处理中 2=创建失败 " 第一步:管理文件已存在,说明另一侧在处理,直接退出 IF file_exist( p_lockfile ) = abap_true. p_rc = 1. RETURN. ENDIF. " 第二步:不存在则创建。创建后立刻回读确认,堵住并发窗口 OPEN DATASET p_lockfile FOR OUTPUT IN TEXT MODE ENCODING DEFAULT. IF sy-subrc <> 0. p_rc = 2. RETURN. ENDIF. CLOSE DATASET p_lockfile. IF file_exist( p_lockfile ) <> abap_true. p_rc = 2. RETURN. ENDIF. p_rc = 0. ENDFORM.

代码分两步,中间那个“创建后再确认一次”容易被省掉。两个作业恰好同时启动时,可能都读到“文件不存在”,然后都往下跑。回读确认能挡住绝大多数情况,虽然严格意义上不是原子操作,但对一天跑两次的批处理来说够用了。

锁分成两把:指图处理一把ORD.LCK,实绩处理一把CNF.LCK。之所以不共用一把,是因为抽出和计上是两个独立方向,ERP 抽指图的时候 MES 完全可以同时写实绩文件,只要写的是不同的文件,没必要互相阻塞。共用一把锁会把处理窗口无谓地拉长,尤其在两边作业时间重叠的时候。

这把锁有个先天短板:程序没有超时机制。进程被强杀、作业被运维取消、机器重启,锁文件都会留在原地,之后所有批次全部被卡。第 5 章给具体的补法。

3. 制造指图抽出:从 ERP 到共享目录的完整链路(ZMAF001)

3.1 抽出条件:五个过滤条件怎么组合

抽指图不是把表里所有指图都倒出来,得按业务状态筛。原文列了五个条件,少一个都会抽出不该发的东西:

  1. 指图的开始日期落在参数文件指定的日期范围内。这个范围是个窗口,参数里配“起始日往后 N 天”,超过窗口的指图不抽,避免把几个月后的计划也推给 MES。
  2. 系统状态是“已发行”,且不是“完成”或“部分完成”。已经做完的指图再推一遍,MES 侧会当成新任务。
  3. 指图没有被取消,是有效指图。
  4. 抽取管理方式二选一:要么日志文件里没有这条指图的发送记录,要么指图的用户状态SEND没被置上。
  5. 上面第 4 条的方式,在启动变式里择一选定,一次运行只用一种,不能混。

为什么给两种管理方式。第一种“日志去重”依赖日志文件本身,好处是不用动业务数据,坏处是日志被清理或归档之后判断就失效了。第二种“用户状态”把发送标记写进指图,好处是持久、可以在 ERP 界面上直接看到,坏处是会占用一个用户状态位,而且状态回写本身有失败风险。项目上选哪种,取决于日志保留多久,以及业务方是否介意多一个状态。

" 抽出主查询:日期窗口 + 状态过滤 + 未发送判断 " 注意:下面两个 NOT IN 是两种管理方式,实际运行时按变式只启用其中一个 SELECT a~aufnr a~aufart a~plnbez a~gamng a~meins a~gstrp a~gltrp a~werks INTO TABLE lt_order FROM aufk AS a WHERE a~werks = p_werks " 工厂,单工厂场景写死也可 AND a~gstrp >= p_date_from " 指图开始日下界 AND a~gstrp <= p_date_to " 指图开始日上界 = 起算日 + N 天 " 系统状态:必须已发行(REL) AND EXISTS ( SELECT objnr FROM jest WHERE objnr = a~objnr AND status = 'I0002' AND inact = space ) " 排除已完成、部分完成 AND NOT EXISTS ( SELECT objnr FROM jest WHERE objnr = a~objnr AND status IN ( 'I0045', 'I0009' ) AND inact = space ) " 方式一:发送日志里没有记录(未发送) AND a~aufnr NOT IN ( SELECT aufnr FROM zif_mes_sendlog WHERE send_flg = 'X' ) " 方式二:用户状态 SEND 未置位(与方式一二选一) AND NOT EXISTS ( SELECT objnr FROM jest WHERE objnr = a~objnr AND status = 'ZMES_SEND' AND inact = space ).

gstrp是指图开始日,用它做窗口判断而不是创建日期,是因为业务关心的是“这批活什么时候要开工”。inact = space这个条件别漏,状态表里同一状态有历史记录,不加这个条件会把曾经置过又取消的也算进来,抽出来的指图数量会莫名其妙多一截。

第 4 条的两个NOT IN在实际代码里不会同时写,按变式分支拼 SQL 或者走两段判断。写在一起只是为了看清逻辑关系,直接照抄会变成“两种方式都满足才抽出”,比预期少抽很多。

3.2 指图文件与构成文件:字段怎么定

文件是纯文本,没有表头定义,字段顺序全靠双方约定。头信息和明细各一份,字段我按工程上常见的组合整理成下面两张表。实际项目里字段数量会更多(客户号、销售订单号、批次、工序号等),但核心结构就是这些:

序号字段类型长度说明
1指图号CHAR 12唯一键,MES 侧主键
2指图类型CHAR 4区分普通、紧急、返工指图
3品目代码CHAR 18要生产的成品
4指图数量NUMC 13.3计划数量,含小数
5基本计量单位UNIT 3与数量配套
6计划开始日CHAR 8格式 YYYYMMDD
7计划完成日CHAR 8格式 YYYYMMDD
8工厂CHAR 4单工厂场景可省

构成品目文件多一个指图行号作为二级键,再加必要量、单位、是否为后台品目标识。后台品目这个字段很关键,第 4 章讲计上的时候会用到。

数值字段的处理有个细节:数量和金额一定要带小数点,不要用“放大 1000 倍存整数”的做法。文本文件的解析端未必知道你的放大倍数,一旦对方按原值读,数量能差三个数量级,而且不报错。

3.3 主程序 ZMAF001 的四段结构

原文特别提到“结构化程序,可读性高”。这在 ABAP 里不是一句虚话,指的是用FORM把主流程拆成职责单一的段,而不是从头到尾写在一个START-OF-SELECTION里。主程序的结构大致是四段:

REPORT zmaf001. START-OF-SELECTION. " 第一段:初始化 + 抢锁。抢不到直接结束,什么也不做 PERFORM frm_init. PERFORM frm_lock USING gv_lockfile CHANGING gv_lock_rc. IF gv_lock_rc <> 0. PERFORM frm_write_start_log USING '对方处理中,本次不执行'. LEAVE LIST-PROCESSING. ENDIF. " 第二段:按变式读参数,抽指图头和明细 PERFORM frm_read_params. PERFORM frm_extract_order CHANGING gt_order. PERFORM frm_extract_component CHANGING gt_cmpt. " 第三段:写文件。全部写成功才继续 PERFORM frm_write_file USING gt_order gt_cmpt CHANGING gv_file_ok. IF gv_file_ok <> abap_true. PERFORM frm_write_log USING 'E' '文件写入失败'. PERFORM frm_unlock USING gv_lockfile. LEAVE LIST-PROCESSING. ENDIF. " 第四段:回写发送状态 + 收尾日志 + 释放锁 PERFORM frm_set_send_status USING gt_order. PERFORM frm_write_end_log USING '正常终了'. PERFORM frm_unlock USING gv_lockfile.

四段的边界很清楚:抢锁、取数、写文件、回写状态。三段之间的顺序不能调,尤其是第三段和第四段。为什么先写文件再回写状态,理由很直接:文件写失败但状态已经置上,这批指图就永久的“已发送”了,MES 永远收不到,而且日志里看起来一切正常。

frm_extract_component单独抽一段,是因为构成品目要根据指图号循环取,取一次头再取一次明细,两次查询的键不一样。放一起会让代码读起来跳跃,出了问题也不好定位是哪一步慢。

3.4 日志双份与 SEND 状态回写的顺序

日志双份不是为了好看。开始结束日志是给运维看的,一天两次,翻起来快,谁跑挂了、几点跑挂的一眼就能看出来。明细日志是给开发看的,按记录逐条写,出了问题拿着指图号去搜,前面几条上下文都在。

回写SEND状态时有个批量处理的细节:不要抽到一条就置一条。整批抽完之后一次性批量置位,中间任何一步失败就整批不置。这不是为了性能,是为了让状态和文件内容保持一致——文件中有一条写了、状态却只置了一部分,下次跑的时候这条指图会再抽一遍,MES 侧就会收到重复数据。

" 状态回写:整批成功之后统一置位,失败则一条都不动 FORM frm_set_send_status USING pt_order TYPE STANDARD TABLE. CHECK lines( pt_order ) > 0. LOOP AT pt_order INTO ls_order. " 调用用户状态设置,把 ZMES_SEND 置为激活 PERFORM set_user_status USING ls_order-aufnr 'ZMES_SEND' 'X'. IF sy-subrc <> 0. " 单条失败不影响其余,但要在明细日志里留痕 PERFORM frm_write_log USING 'E' |指图 { ls_order-aufnr } 状态回写失败|. ENDIF. ENDLOOP. ENDFORM.

单条状态回写失败不中断整批,但要记日志。原因是一旦中断,前面已经置位的指图就困在“已发送”状态,而文件里可能还没写全,反而造成更麻烦的数据不一致。留痕之后人工补置,比整批回滚可控。

3.5 变式参数:哪些必须外置

原文把“运行环境配置变式化”列为这个附加程序的第一个特点,列出来的有换行代码、文件逻辑路径、日志保留期限。实际落地时我建议把下面这些全部外置,一项都别写死在代码里:

参数含义常见取值不配的后果
逻辑路径out / in / log / ctrl 四个目录由 BASIS 配物理映射换环境要改代码重新传
换行代码CRLF 或 LFWindows 端 CRLF对方读到一整行,解析全错
日志保留天数明细日志滚动周期30 / 90 天盘满,或者重跑时日志已清
日期窗口开始日往后 N 天3 / 7 / 30抽太多拖慢 MES,或漏抽
抽取管理方式日志去重 / 用户状态二选一两种同时生效,逻辑混乱
工厂单工厂代码业务给定跨工厂数据混在一起

逻辑路径这条尤其重要。开发环境、测试环境、生产环境的挂载点肯定不一样,写死之后每次上线都要改一次代码,改出问题就是事故。走逻辑路径 + 变式,换环境只改变式。

4. 实绩计上:MES 回传的数据怎么安稳落进 ERP(ZMAF005)

4.1 MES 侧的输出约定:汇总口径和确认区分

实绩回传有两个约定,写死在业务流程里,接口层面只是照着实现。

第一个是汇总口径:MES 侧按制造指图为单位,按日汇总。哪怕 MES 内部把指图拆成十张工序票发到十个工位,回传的时候也要合并回一张指图。ERP 侧不再处理拆分合并,收到什么就是什么。这个约定省掉了 ERP 侧大量的归并逻辑,代价是把复杂度压给了 MES。现实里 MES 厂商经常不愿意做这个归并,谈合同的时候这一条要写清楚。

第二个是确认区分:完成数据里带一个“部分确认 / 最终确认”的标识。ERP 侧据此判断这条指图是继续挂着还是可以关掉。少了这个标识,ERP 只能靠数量去猜指图有没有做完,猜错的概率不低。

投入实绩里还有一条限制:后台倒冲品目的投入数量如果由 MES 回传,ERP 侧直接判错,写日志,不登记。原因是后台倒冲本来就是在 ERP 侧按 BOM 自动扣的,MES 再传一次就重复了。要改后台倒冲的数量,得走 ERP 的作业确认界面手工改,不能从接口进来。

4.2 实绩文件结构:唯一键怎么设计

实绩文件同样分两份,投入和完成。字段结构大致如下:

序号字段类型长度说明
1指图号CHAR 12关联键
2作业号NUMC 8指图下的工序
3记录种别CHAR 2IS=投入 / DN=完成
4确认日期CHAR 8YYYYMMDD,按日汇总用
5投入品目代码CHAR 18仅投入记录有值
6投入数量NUMC 13.3仅投入记录有值
7确认产量NUMC 13.3仅完成记录有值
8确认不良数NUMC 13.3仅完成记录有值
9确认区分CHAR 1P=部分 / F=最终
10预计完成日CHAR 8仅最终确认时有意义

唯一键由“指图号 + 作业号 + 记录种别 + 确认日期 + 序号”组合而成。加序号是为了处理同一天同一指图多次报工的情况,MES 侧自己递增。这个键是整套幂等逻辑的基础,一定不能省。

原文里有一句很关键:“接口文件中的每条记录都持有唯一键,读入侧据此判断是否为重发数据,重发的记录不再处理。” 这一句话解释了为什么要有唯一键,也解释了为什么 ERP 侧不能简单地“读到什么就记什么”。

4.3 主程序 ZMAF005:先落工作库再计上

ZMAF005的主流程比ZMAF001多一步。抽出程序只要把数据写出去就行,计上程序必须考虑“处理到一半挂了怎么办”。原文的写法是:先把读进来的数据落到一张工作库表,再从工作库往业务表计上。

START-OF-SELECTION. PERFORM frm_init. PERFORM frm_lock USING gv_lockfile CHANGING gv_lock_rc. IF gv_lock_rc <> 0. PERFORM frm_write_start_log USING '对方处理中,本次不执行'. LEAVE LIST-PROCESSING. ENDIF. " 第一步:读文件 → 落到工作库,按唯一键去重 PERFORM frm_read_file CHANGING gt_raw. PERFORM frm_check_done CHANGING gt_raw. " 剔除已处理过的记录 PERFORM frm_save_to_work USING gt_raw. " 第二步:按工作库逐条计上,单条失败只记日志不中断 LOOP AT gt_raw INTO ls_rec. PERFORM frm_post_issue USING ls_rec CHANGING gv_ok. IF gv_ok <> abap_true. PERFORM frm_post_done USING ls_rec CHANGING gv_ok. ENDIF. IF gv_ok = abap_true. PERFORM frm_mark_processed USING ls_rec. " 标记已计上 ELSE. PERFORM frm_write_log USING 'E' ls_rec-msg. " 记日志,继续下一条 ENDIF. ENDLOOP. " 第三步:全部处理完才删文件、写结束日志、放锁 PERFORM frm_delete_file USING gv_infile. PERFORM frm_write_end_log USING '正常终了'. PERFORM frm_unlock USING gv_lockfile.

顺序上有三处不能动。工作库落盘在计上之前,是为了异常中断后能重跑而不重复;删文件在全部处理完之后,是为了中途挂了下次还能重读;放锁在最后,是为了中途挂了锁还在,能挡住下一轮,避免在数据不完整的情况下继续处理。

4.4 二重计上的三道防线

重复计上是文件型接口最典型的故障,一套设计至少要设三道防线。

第一道是唯一键去重。读文件时先查工作库和已处理记录表,键已存在就跳过,不报错。跳过而不报错是因为重发本身是正常操作,不是异常,报错会把日志淹掉。

第二道是工作库落盘。计上动作执行之前,记录已经进了工作库并提交。中途崩溃重跑时,可以从工作库恢复现场,知道哪些做了、哪些没做。

第三道是文件处理标记。每条记录处理完之后写一个已处理标记,全部处理完再删文件。这三层叠起来,才能保证“最多处理一次”。

原文里还有一条约定值得学:整个接口没有提供“修改已计上数据”的功能。计上完成之后要改数量,只能走 ERP 的作业确认界面手工改。这条看着像是功能缺失,实际上是为了把数据的唯一所有权收回到 ERP 侧。接口只负责把数据送进来,送进来之后归 ERP 管,MES 不能反复覆盖。

4.5 错误分类:哪些记日志跳过,哪些整批停

原文列了三类会报警的错误,处理方式都是“记日志、不登记、继续处理下一条”:

  • 后台倒冲品目的投入实绩被 MES 回传。数量应当由 ERP 自动倒冲,接口传进来的判错。
  • 构成品目在指图发行时没有登记在 BOM 里。BOM 里没有的料,计上去也对不上。
  • 数量、单位不合法,或者指图号在 ERP 里查不到。

这三类的共同点是“单条数据有问题”,影响范围限于这一条,不该拖累整批。整批停的只有两种情况:文件读不进来,或者工作库写不进去。前者是通道问题,后者是数据库问题,继续跑没有意义。

日志里记错误的时候,别只写一句“计上失败”。至少要带指图号、作业号、记录种别、错误原因,能定位到具体哪一条。见过日志里只写“处理异常”的,翻半天不知道是哪条数据,最后只能把整批文件重新导一遍手工比对。

5. 排查手册:文件型 MES 接口最容易翻车的五个地方

5.1 文件明明在,对方就是说没收到

现象:ERP 侧日志显示文件写入成功,ls -l也能看到文件,MES 侧却反馈目录是空的,或者文件名对不上。

原因:两边的逻辑路径指向了不同的物理挂载点。常见的情况是 ERP 应用服务器和 MES 服务器各挂了一个同名目录,实际不是同一块存储;或者一边用大小写敏感的文件系统、一边不敏感,写入ORDER_HEAD.txt、读的时候找order_head.txt;再或者是权限问题,文件写进去了但对方账号没有读权限。

解决:上线前做一次双向验证——ERP 写完在 MES 机器上用ls和stat各看一次,确认 inode、文件大小、修改时间完全一致;逻辑路径的参数在两端各打印一次实际物理路径,写进上线检查表。文件名统一用大写加下划线,避开大小写问题。

5.2 重跑一次,MES 收到两遍指图

现象:作业因为网络抖动失败重跑,MES 侧同一批指图变成了两份,现场按两份排产。

原因:SEND状态回写放在了文件写入之前。文件写入其实失败了,状态却已经置成“已发送”,重跑时靠状态判断就认为没发过;或者选的是“日志去重”方式,而日志保留期限只有 7 天,重跑发生在第 8 天,日志已经滚动清理,程序判断为“未发送”。

解决:状态回写严格排在文件写入成功之后,中间任何一步失败都整批不置位。选日志去重方式时,日志保留天数必须大于“最长可能的重跑窗口”,至少留 30 天。MES 侧也加一层按指图号去重,双保险。

5.3 文件里全是乱码,程序不报错但数据全错

现象:MES 侧读到的品目代码、单位字段是乱码,程序照常处理,计上之后数量对不上。

原因:原文明确写了接口文件不做编码转换,原样透传。ERP 侧按一种编码写,MES 侧按另一种编码读,中间没有任何校验。这种情况程序不会报错,因为对程序来说就是一堆字节。

解决:两端约定统一编码,写进接口文档,不能靠“应该一样”。文件第一行放一个固定标识串(纯 ASCII),读入时先校验这一行,对不上直接拒绝处理并报警。换行代码同样在变式里明确指定,不要靠程序自动识别。

5.4 管理文件残留,一整晚作业全跳过

现象:所有批次都显示“对方处理中,本次不执行”,接口文件堆了一整天没人处理。

原因:上一轮作业被强杀、被人为取消,或者服务器重启,管理文件ORD.LCK留在ctrl目录里没删掉。程序只判断“文件在不在”,没有超时机制,也没有任何告警。

解决:管理文件里写入创建时间和进程标识;启动时先读时间戳,超过阈值(比如 2 小时)判定为残留锁,记一条告警日志后强制清除并继续。同时加监控,连续 N 次启动都因为锁退出就发通知。人工解锁的步骤要写进运维手册,不能靠开发临时上机删文件。

5.5 后台倒冲品目一计上就报错

现象:实绩文件里只要带了后台倒冲品目的投入记录,对应那条就失败,日志里全是错误。

原因:业务上后台倒冲品目的消耗由 ERP 在确认时按 BOM 自动扣减,MES 不需要也不应该回传。MES 侧把工位上所有领用都汇总上来了,没有区分倒冲品目。

解决:治本的做法是在 MES 侧输出时就把后台倒冲标识过滤掉,这个标识在指图抽出时已经随构成品目文件传过去了。ERP 侧保留这道校验作为兜底,遇到就记日志跳过,不中断整批。两边都做,比只做一边稳。

6. 二次开发:把这套骨架改成你自己的接口

这套设计是二十多年前的产物,文件通道和批处理在今天未必是最优解,但里面几个结构性决定到现在依然成立:唯一键做幂等、先落工作库再计上、状态回写排在文件写入之后、锁和接口文件放同一挂载点。改的时候别把这几条一起丢掉。

真要动手,我一般按三块改。第一块是通道,把文件换成中间表或者消息队列都行,但要把“读进来先落库、落库之后再处理”这个模式保留下来。文件通道的优势是可追溯、可人工补,换成消息之后这两点会丢失,得在中间表里补一个原始报文列存着,出问题还能回放。第二块是参数,把逻辑路径、日期窗口、日志保留、抽取方式这些全部从代码里挪到配置表,换环境不重新传程序。第三块是键,原来的唯一键是“指图号 + 作业号 + 记录种别 + 确认日期 + 序号”,多系统接入之后要加“源系统标识”,否则两个 MES 或者一个 MES 多条产线并行时会撞键。

验证方法我用一套固定脚本,每次改完都跑一遍:造三条完全相同的记录验证幂等、造一条后台倒冲品目验证错误分支、造一条不存在的指图号验证查不到指图时的日志、人为删掉管理文件验证锁失效、把换行代码改成另一种验证解析。五条全过才算改完,少一条都不敢上线。

改造点原方案建议做法
传输通道共享目录文本文件中间表 / 消息队列,保留原始报文列
幂等键指图号 + 作业号 + 日期 + 序号前面加源系统标识
配置变式参数配置表 + 变更留痕
锁机制管理文件存在性判断文件内写时间戳 + 超时自动清理 + 告警
日志两级文本日志保留两级,明细日志入库便于检索

那次锁文件残留的事故之后,我每次接手文件型接口,第一件事都是打开ctrl目录看一眼有没有陈年旧锁,第二件事是翻日志确认最近三天的批次都真的跑过,而不是“显示成功”。这两步花不了五分钟,能挡掉大部分半夜的电话。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进