ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

三菱PLC ST语言字符串处理:CONCAT与REPLACE在工单拼接中的实战解析

三菱PLC ST语言字符串处理:CONCAT与REPLACE在工单拼接中的实战解析 做设备管理的项目做久了你会发现一个规律越是看起来不起眼的功能越容易在关键时候卡你一下。三菱PLC的ST语言里字符串处理就是典型例子。工单编号拼接、批次号生成、MES数据上报、刀具寿命追溯——这些现场最常见的需求全都压在CONCAT、REPLACE这几个字符串函数上。很多人写梯形图写了几年一碰到ST语言里的字符串操作就发怵总觉得不如C语言顺手。其实三菱的ST字符串函数没有你想象的那么“反人类”只是它的参数风格和习惯用法跟通用编程语言有区别。这篇文章就围绕工单拼接这个真实场景把CONCAT和REPLACE这两个函数的使用逻辑、参数细节、常见坑全部过一遍顺便聊聊我在FX5U平台上实测下来的一些心得。内容主要面向设备工程师和调试人员不管你是刚接触ST语言还是已经在项目里被字符串折磨过几次看完应该都能直接照着做。1. 为什么工单拼接要单独拿出来说1.1 工单拼接到底解决什么问题工单拼接不是一个演示用的“Hello World”它直接对应现场的追溯体系。比如一条自动化产线每加工一个工件系统要把订单号、物料编码、炉批号、生产日期、班次、序号全部拼成一个唯一的序列号然后通过扫码枪或MES接口上传。这个序列号一旦拼错一位追溯链就断了后面客户投诉、质量问题回溯全部没法做。更麻烦的是工单格式往往不是一成不变的——客户改了编码规则、现场换班需要追加班次标识、日期格式要从“20240901”改成“24-09-01”这些改动都要在PLC程序里快速完成。CONCAT负责把零散字段拼起来REPLACE负责在已有字符串里做局部替换修正两者组合起来基本可以覆盖工单拼接的绝大多数场景。我见过不少工程师为这个需求去写一大堆MOV指令配合ASCII码换算代码又长又难维护其实用ST字符串函数几行就搞定了。1.2 三菱ST语言字符串处理能力概览三菱的ST语言结构化文本在GX Works3里被定位成IEC 61131-3标准语言但它在字符串处理上做了一些自己的封装。最核心的一点是字符串在三菱ST里不是“字符数组”而是一个带长度的STRING类型长度上限在变量声明时确定。这一点跟C语言差别很大写惯了C的工程师往往会在长度上限上吃亏。三菱ST内置的字符串函数主要包括CONCAT拼接、REPLACE替换、LEFT/RIGHT/MID截取、INSTR查找、LEN取长度等基本覆盖了日常需求。但它的函数命名和参数顺序跟西门子SCL、跟C语言标准库都不一样比如REPLACE的入参是“原字符串起始位置替换长度替换内容”而不是C语言里那种“全量字符串替换”。这种差异如果不注意写出来的程序就是逻辑正确但结果完全不对。1.3 CONCAT与REPLACE的分工定位在工单拼接这个场景里CONCAT和REPLACE承担的是不同阶段的工作。CONCAT负责“从零到一”的构建把多个独立字段按顺序装填到一个目标字符串里。REPLACE负责“从一到优”的修正工单拼完之后发现格式不对、中间某段需要换成新值或者要在固定位置插入工厂代号这时候CONCAT未必方便REPLACE反而更合适。举个例子一个工单已经拼成“P12345-240901-A-001”现在客户要求把中间的日期段从“240901”改成“24-0901”你不可能把整个字符串拆开重新拼一遍REPLACE直接从指定位置替换就行了。实际项目里我经常把两者串起来用先用CONCAT拼出基础工单再用REPLACE做格式修正。这个思路能让程序结构清晰很多现场改规则的时候也不用翻一大片梯形图。2. 三菱ST字符串函数核心解析2.1 CONCAT函数语法与特性CONCAT在三菱ST里的作用是把两个字符串首尾相接合成一个。它的基本调用方式是直接在ST程序里写result : CONCAT(str1, str2);其中str1和str2既可以是字符串变量也可以是字符串字面量。有一点必须强调三菱ST的CONCAT只支持两个入参这一点跟其他编程语言里的多参数拼接不同。你想拼三个字段就得嵌套调用CONCAT(CONCAT(a, b), c)。我见过有人在GX Works3里试着写CONCAT(a, b, c)编译直接报错还以为是软件出问题了。另外三菱ST的赋值符号是:不是等号很多初学者在这上面栽跟头。CONCAT的拼接结果要写到一个STRING变量里这个变量的长度声明必须足够大否则运行时会把超出部分截断而且不会有任何报警提示。我在FX5U上实测过声明的字符串长度是16个字符结果拼了20个字符进去最后只保留前16个后面静默丢掉。这种“安静的截断”是最坑人的因为它不会让程序停跳也不会触发错误标志只有你核对数据内容时才会发现不对。所以用CONCAT之前先算清楚最长会拼出多长的结果尽量给目标字符串留出冗余余量。还有一个实用技巧CONCAT可以处理字符串变量和字面量的混合拼接比如result : CONCAT(P, CONCAT(partNo, -A));这种写法在构建固定格式头时非常方便不需要为每个固定段单独建变量。2.2 REPLACE函数语法与特性REPLACE在三菱ST里的作用是从源字符串的指定位置开始把指定长度的字符替换成新内容。它的调用格式是result : REPLACE(source, startPos, replaceLen, newStr);。注意这里的startPos从1开始计数不是0这是跟C语言差异最大的一点。比如源字符串是“P12345-240901-A-001”想从第8个字符开始替换4个字符把“2409”换成“24-0”写出来就是REPLACE(src, 8, 4, 24-0)。实际执行效果是把原位置4个字符先去掉再插入4个新字符长度保持不变。但如果新的内容跟被替换长度不一致结果字符串的长度就会变化。比如替换长度为4新内容是“24-09-01”共8个字符那最终字符串就会变长。这意味着REPLACE不只是“替换”它本质上是“先删除再插入”。理解了这一点很多边界问题都能想通。REPLACE的startPos加上replaceLen如果超出源字符串总长度三菱的处理是替换到字符串末尾为止不会报错。但如果你startPos本身大于源字符串长度那REPLACE会直接返回源字符串等于什么都没发生。这个行为我专门测试过不同版本固件可能略有差异稳妥的做法是在调用前用LEN函数自己判断一下避免在这种边界上出幺蛾子。REPLACE还有一个经常被忽略的用途它可以在指定位置“插入”内容。只要把replaceLen设为0比如REPLACE(src, 5, 0, -X)意思是“在第5个字符位置前插入-X”。这招在做工单扩展时特别好用不用重新拼接整个字符串。2.3 两者对比与选型原则CONCAT和REPLACE虽然都跟字符串变长变短有关但它们的适用场景有清晰边界。我整理了一个对比表项目里用的时候可以直接对着选对比维度CONCATREPLACE核心动作末尾追加拼接指定位删除并插入入参个数2个字符串4个参数源串位置长度新串位置计数无位置概念从1开始是否改变长度结果长度两串长度之和取决于替换长度与新内容长度的差值典型用途多字段组装、拼接头尾固定段格式修正、局部替换、指定位置插入边界风险目标变量长度不足会静默截断起始位置越界会原样返回多段拼接需要嵌套调用不需要但一次只处理一个位置选型原则其实很简单如果你手上是“一堆要组装的数据字段”那就用CONCAT一层层套如果你手上是“一个已经存在的字符串需要在特定位置动刀”那就用REPLACE。两者不是竞争关系更多是配合关系。我实际项目里最常见的手法是用CONCAT拼好基础工单然后用REPLACE做日期格式修正、在固定位置插入工厂代码。这样分工明确程序可读性也高。还有一点要注意三菱ST里的字符串比较不是用双等号而是用但字符串比较时是否会区分大小写取决于CPU固件设置FX5U默认不区分大小写Q系列某些版本区分。这个差异在做工单判重、扫码对比时非常关键建议在项目早期就把这个行为确定下来并写在设计文档里。3. 实操基于FX5U的工单拼接完整案例3.1 场景需求与变量规划我把这个案例设定成一条真实的自动化装配线场景每完成一个工件PLC需要生成一个唯一工单号格式为“工厂代码-订单号-日期-班次-序号”。具体要求是工厂代码固定为“F01”订单号由上位机下发的D100~D103四个16位字组成日期从PLC时钟读取格式是“YYMMDD”班次是一个字符“A”或“B”序号是计数器C0的当前值要求补足3位不足前面补0。最终工单示例F01-2235-240915-A-007。拿到这个需求第一步不是写代码而是规划变量。在GX Works3的ST程序里新建局部变量我做了如下声明SrcOrder : STRING(8)接收订单号字符转换结果DateStr : STRING(8)日期字符串ShiftStr : STRING(2)班次标识SeqStr : STRING(4)序号字符串WorkNo : STRING(32)工单最终结果TempStr : STRING(32)中间拼接缓冲区这里有个细节WorkNo声明为STRING(32)而不是STRING(24)表面上看24个字符就够用了但我故意留了8个字符的余量。因为现场规则随时可能变比如客户后来要在订单号后面加产品版本号如果长度刚好卡死改起来就要动数据结构留余量能省掉一次程序升级。这种习惯我建议从刚开始写ST就养成——字符串长度是程序里最不该“精打细算”的资源。另外三菱的STRING类型在声明时括号里的数字指的是“字符个数”不是字节数中文字符要特别注意。FX5U的字符串内部按UTF-8或者UCS-2编码取决于设置但STRING长度计数是按字符算的一个中文算一个字符。细节后面问题排查部分会专门说。3.2 使用CONCAT实现基础拼装变量规划好后就可以写ST逻辑了。第一步是把D100~D103里的订单号拼成一个字符串。上位机下发的订单号是数值型不能直接进CONCAT需要先用整数转字符串函数INT_TO_STRING。三菱ST里这个函数的名称是INT_TO_STRING或DINT_TO_STRING具体看数据类型。这里有一个关键点INT_TO_STRING转换出来的字符串是“原样数字”不会自动带千分位也不会补前导0所以订单号是2235就直接转换出“2235”。如果订单号本身有前导0需求比如要求显示“02235”那就得自己补0。接下来写基础拼接程序WorkNo : F01-; WorkNo : CONCAT(WorkNo, INT_TO_STRING(D100)); TempStr : CONCAT(WorkNo, -); WorkNo : CONCAT(TempStr, DateStr); TempStr : CONCAT(WorkNo, -); WorkNo : CONCAT(TempStr, ShiftStr); TempStr : CONCAT(WorkNo, -); WorkNo : CONCAT(TempStr, SeqStr);这段代码的写法比较“笨”但逻辑非常透明。每拼一段就放到TempStr过渡再继续拼下一段。实际程序里你也可以用更紧凑的嵌套写法比如WorkNo : CONCAT(CONCAT(CONCAT(F01-, INT_TO_STRING(D100)), -), CONCAT(DateStr, CONCAT(-, ShiftStr)));一次完成但嵌套太多层之后现场维护的人读起来会非常吃力。我建议的原则是如果拼接段数超过3段就老老实实用中间变量分步拼牺牲一点代码美观度换来的是调试时可读性。拼装完之后WorkNo的内容就是“F01-2235-240915-A-007”这样的字符串了。但这个程序有个潜在问题DateStr、SeqStr从哪里来还没处理下一节专门讲格式修正。3.3 使用REPLACE修正工单格式日期字符串和序号补零这两个环节是REPLACE真正发挥价值的地方。先说日期。三菱PLC读取时钟可以用内置函数CLOCK或直接读特殊寄存器D8018年、D8019月、D8020日FX5U还能通过RD_TIMER读取。为了拿到“YYMMDD”的紧凑格式我先把年月日分别转成字符串然后拼接但这里会遇到一个典型问题某个月是9月时转换结果只是“9”而要的是“09”。两个办法一个是拼接前判断月数小于10就在前面补0另一个就是用REPLACE。我推荐第二种思路更通用。做法是先拼一个占位串“YYMMDD”比如先用字符串“240900”然后通过REPLACE把月段位置的内容替换掉。具体流程是预先准备年月日各自补好0的字符串然后拼到一起。补零本身可以用REPLACE实现MonthStr : REPLACE(00, 2, 1, INT_TO_STRING(monthVal));意思是在字符串“00”的第2个位置替换1个字符把月份数字放进去。这个方法比IF判断嵌套简洁得多。序号补零同理计数器值是7要显示“007”可以先准备“000”然后用REPLACE(000, 3, 1, INT_TO_STRING(countVal))把最后一位替换成7得到“007”。但如果计数器值超过9变成两位数替换1个位置就不够了。更稳妥的方法是用CONCAT先判断序号是否大于99小于10就拼两个0小于100就拼一个0。REPLACE更适合固定位置的格式修正补零这种“头部填充”需求用CONCAT拼前导零更直观。所以在这个例子里我的最终实现是日期用REPLACE修月日格式序号用CONCAT补前导0。两者结合的思路是能用REPLACE在固定位置动刀的地方就用REPLACE能用CONCAT在头部做填充的地方就用CONCAT。这也是我前面说的“分工定位”在实际代码里的落地。3.4 关键参数与执行细节整个拼装程序写完后有两个执行细节必须落实。第一是字符串变量的初始化。三菱ST里的STRING变量如果声明后没有赋值初始内容是空字符串但如果你是复用中间变量上一次执行残留的内容会留在里面。比如TempStr在扫描周期1里保存了“F01-2235-”如果扫描周期2执行时拼接流程只走了前半段就跳过了TempStr还是旧内容后续把这个变量拿来当基座继续拼结果就会叠加上历史值工单号越变越长。我的习惯是在每个扫描周期开始把所有用到的字符串中间变量统一清空写成TempStr : ;。这个习惯救了我不止一次。第二是字符串拼接的扫描周期问题。CONCAT函数在ST里是当扫描周期内立即执行的不存在异步延迟所以不用担心跨周期数据不一致。但如果工单号生成后马上要发给上位机建议在工单号生成完成位置加一个“生成完成”标志位上位机看到标志位再读字符串。不要在生成过程的中间节点去读工单号那样读到的可能是半成品。另外把工单号写入HMI或触摸屏时注意触摸屏侧的字符串变量长度也要留够不然显示会被截断。我在一个项目里见过PLC侧WorkNo是STRING(32)但HMI组态时那个变量只设了16个字符结果界面显示就少了后半段排查了半天还以为是通讯问题——其实就是两边长度定义不一致。4. 常见问题与排查技巧实录4.1 字符串长度到底炸没炸字符串长度溢出是工单拼接里最隐蔽的问题。很多工程师有个错觉GX Works3编译都通过了程序运行也正常就觉得字符串没毛病。但实际上编译检查只能确认语法和数据类型匹配不会检查运行时长度。我在2.1节提到过字符串写入超过STRING声明的字符数时多出来的部分会被直接截断CPU不报任何错误。这个问题在调试界面特别有迷惑性——你在监视窗口看WorkNo的值看到的是“F01-2235-240915-A”以为程序只生成了这么多其实是因为后面“-007”被截掉了。排查方法很简单在最终赋值的位置再加一个LEN(WorkNo)的监视比对实际长度跟期望长度是否一致。更稳妥的做法是在程序里做显式检查IF LEN(WorkNo) ExpectedLen THEN一旦长度不对就置一个报警位。还有一个小技巧把字符串变量声明得比预期长一些之后如果现场规则变化导致拼接内容变长程序大概率不会炸但如果声明的时候刚好卡死任何一个小改动都可能带来连锁问题。经验值是留出20%到30%的余量。4.2 中文乱码与编码问题工单拼接遇到中文是另一个重灾区。三菱FX5U的字符串内部编码方式在不同固件版本和不同设置下有差异GX Works3里字符串默认按UTF-8处理但如果你把字符串通过MODBUS TCP传给上位机上位机那边如果按GBK解码中文就会乱码。再加上STRING长度的“字符数”和“字节数”不一致的问题——UTF-8编码下一个中文占3个字节但在STRING里计1个字符——就很容易出现长度判断和实际传输字节数不一致的情况。我在项目里遇到过工单里含客户中文名称PLC侧LEN函数返回13但发到上位机的报文里字节数却是35上位机按协议解析就错位了。解决思路是工单号这类要跨系统传输的数据尽量用纯ASCII字符如果实在要带中文一定要把编码格式在通讯协议里写死并要求上位机按UTF-8解析。另外要注意三菱ST的字符串比较函数在处理中文时也受编码影响两个看起来一样的字符串用比较返回FALSE往往就是因为编码细节不一致。建议中文只用于界面显示不参与逻辑判断和工单生成。4.3 REPLACE的边界行为REPLACE函数的边界行为我在2.2节提了一部分但这里要专门展开。当startPos等于源字符串长度1且replaceLen为0时REPLACE的效果等同于在字符串末尾追加内容。这个特性可以用来做追加但我实测时发现在FX5U固件版本1.210以下这个用法偶尔会触发异常建议不要在关键逻辑里用REPLACE做末尾追加老老实实用CONCAT。当replaceLen大于源字符串从startPos开始到末尾的剩余长度时三菱的处理是把剩余部分全部替换掉。比如源字符串“ABCDEFGH”从第3个位置开始替换6个字符新串是“XY”结果会变成“ABXY”F到H会被直接删除——因为原位置只剩6个字符CDEFGH全部被替换成2个新字符。这个行为如果没意识到很容易在计算最终长度时出错。我踩过的坑是用一个固定长度的替换内容去修正工单结果工单长度因边界截断变短了后续提取子串的MID函数位置全部错位。从那以后我写REPLACE之前都会先用LEN确认源字符串长度和替换范围避免想当然。4.4 工单拼接问题速查表把我在多个项目里实际遇到过的字符串问题整理成一个速查表下次可以直接对照排查现象可能原因排查要点工单号后半段莫名消失目标STRING声明长度不足检查WORKNO的声明和LEN返回值两个工单号看起来一样但比较不等编码或尾随空格差异用INSTR/LEN核对尾部字符REPLACE后长度不符合预期replaceLen设置过大或startPos越界计算替换区段实际剩余长度字符串拼接后偶尔出现前次残值中间变量未清零周期开始时统一清空字符串变量中文传输到上位机乱码UTF-8与GBK编码不匹配协议固定编码并在两端统一日期月份“5”显示成“5”而非“05”整数转字符串未补前导0用CONCAT或REPLACE补零程序运行后工单号不刷新拼接逻辑被跳周期执行检查生成使能条件的扫描周期行为触摸屏显示工单号被截断HMI变量长度小于PLC长度两边长度定义保持一致这张表不能覆盖所有问题但能覆盖我遇到过的90%场景。真正重要的是建立“字符串问题往往不会报错”的意识对所有工单类字符串数据都要主动核对长度和内容而不是依赖监控窗口“看着差不多就行”。另外在程序里针对工单号单独做“长度校验内容校验”其实成本很低就是几条比较指令的事但能极大减少现场数据追溯的隐患。4.4.1 一个另类的CPU扫描周期问题严格来说这不算是字符串函数本身的问题但它会表现得像字符串问题。我的一个老项目中工单拼接逻辑放在了一个由M100触发的ST程序块里M100是上升沿脉冲。结果生产节拍快的时候M100的脉冲宽度太窄ST块还没执行完整个拼接流程脉冲就消失了导致工单号只拼了一半就把结果发出去。表面看是“字符串拼接不完整”实际是触发方式有问题。后来我改成“置位标志位开始拼接拼接完成复位标志位”彻底解决了。这个经验说明工单拼接这类多步逻辑一定要用一个内部状态位来保证“从开始到结束的完整性”而不是依赖外部短脉冲。字符串本身不会跑飞但执行时机不对它就会给你一个“看起来坏掉了”的半成品。5. 扩展让工单拼接更健壮的高级用法5.1 其他字符串函数配合使用CONCAT和REPLACE不是万能的工单拼接里还经常用到另外几个函数。MID函数可以从字符串中截取子串比如要从完整工单“F01-2235-240915-A-007”里单独取出订单号“2235”用MID(WorkNo, 5, 4)就能拿到。INSTR函数可以查找某个子串在源字符串中的起始位置比如想知道“-A-”在工单里的位置用INSTR(WorkNo, -A-)返回7。这两个函数配合REPLACE可以实现“动态定位后替换”——先INSTR找到位置再根据这个位置做REPLACE。这在工单格式不固定、但关键分隔符固定的场景里特别有用。比如客户的订单号长度会变化你不能写死从第5个字符取订单号但你可以先INSTR找到第一个“-”的位置然后用MID从第一个“-”后面开始取。这种组合用法熟练之后工单解析和格式转换就变得越来越通用。还有LEN函数它不仅用来检查长度还可以用来做“字符串末尾去空格”的判断——三菱ST没有内置TRIM函数但可以通过LEN配合MID从尾部循环判断字符是否为空格来实现。5.2 用结构化数组管理多工单当产线同时跑好几个订单时单个字符串变量就不够用了。我的做法是声明一个字符串数组WorkNoArr : ARRAY[0..9] OF STRING(32);。每完成一个工件就用上面的拼接逻辑生成工单号按序号放入数组对应位置。这里有个重要的细节三菱ST的数组元素可以是STRING类型但数组下标必须用INT类型变量不能用WORD。而且数组在初始化时会一次性把所有元素清空这个初始化动作本身会占用扫描周期时间在数据量大的时候要注意。用数组管理工单后上位机可以通过批量读取方式一次拿走10个工单号通讯效率比逐个读取高很多。但是要特别注意数组元素的数量不要超过CPU的字符串内存上限FX5U的STRING总容量在参数里可以配置默认值对于一般工单场景够用但如果你定义了几百个STRING(128)的数组内存占用会非常可观PLC可能直接启动不了。我在项目里就因为数组开得太大遇到过程序下载后CPU的ERROR灯常亮的情况最后查出来是STRING区域配置不够增大设置后才恢复正常。5.3 工单拼接程序的模板化封装当你在几个项目里都写了类似的工单拼接逻辑后最好的做法是把它封装成一个标准功能块。三菱ST里可以用FUNCTION或FUNCTION_BLOCK来封装。我建议用FUNCTION_BLOCK因为工单拼接经常需要保存中间状态而且需要多个输出。封装思路是把“字段输入”作为FB的输入变量“工单输出”和“完成标志”作为输出变量“中间步骤标志位”作为内部变量。这样之后写新项目的工单逻辑直接拖一个FB实例填上订单号、日期、班次、序号输出就是完整的工单号不用每个项目都从头写一遍。封装的时候建议把“格式规则”也作为FB的参数比如工厂代码、分隔符、日期格式都提成输入参数这样不同产线的差异就能靠参数配置来兼容而不是靠改代码。这种模块化的做法长期看能省下很多调试时间。我自己在这个FB里还会加一个“测试模式”输入置ON时固定输出一个预置的测试工单号方便上位机联调这个设计在现场调试阶段帮了大忙。6. 个人实操体会与小技巧工单拼接看起来是小事但它直接影响生产数据的完整性和可追溯性出问题的时候往往是最着急的。我在几个项目里练出来的体会是字符串操作要当作“有状态的数据流”来管理而不是“一次性的计算”。什么意思呢就是每个周期你都要清楚地知道你的字符串变量当前处于什么状态是已经拼好了还是没拼、是干净的还是带残留。这比学会某个函数的语法重要得多。每次写字符串拼接程序我都会在心里过一遍这几个问题这个变量的长度余量够不够中间变量清空了吗生成完成标志位跟数据更新是同步的吗如果编码格式涉及中文通讯协议里说清楚了吗最后再分享一个很实用的小技巧在GX Works3的监视模式下字符串变量的显示往往只显示前几个字符调试的时候很容易造成“程序只有这么长”的错觉。想看完整内容把监视窗口拉宽或者直接把LEN(变量)也加到监视列表里两个放在相邻位置一起看。如果LEN等于你期望的值内容大概率是对的如果LEN不对再去找拼接逻辑里的问题。这招虽然简单但真的能省掉很多无头苍蝇一样的排查时间。工单拼接这件事说难不难说简单不简单。把CONCAT和REPLACE用熟把长度和边界条件管好你会发现三菱ST的字符串处理完全可以做到既灵活又可靠。
返回列表