资讯详情

资讯详情

S7-1200/1500背景数据块选型:单个实例、多重实例与参数实例

做产线电控这行十几年我见过太多项目在数据块这一层栽跟头。同样是一套 S7-1200/1500 的程序有人写得清清爽爽DB 号从个位数排到二十几号在线监控时哪个变量在哪儿一目了然有人一打开项目树背景数据块密密麻麻铺满屏幕改一个参数要翻三四个 DB最后连自己都不敢动。这中间的差距很多时候不在于谁更懂指令表而在于有没有把单个实例、多重实例、参数实例这三种背景数据块的用法分清。这三个词在手册里都有但手册讲的往往是语法上怎么写而不是工程上该选哪个。我带过的新人里十个有八个能背出多重实例的定义却在实际项目里把它用成了唯一方案结果一个主 FB 的背景 DB 干到几十 KB下载一次几十秒在线改个接口整个 DB 推倒重来。还有一种情况更隐蔽用了参数实例但调用顺序没理清两份数据互相踩运行几天才冒出莫名其妙的故障。这篇内容就是把这三种实例方式从头拆一遍——它们的内存放在哪、编译后长什么样、什么场景该用哪个、哪些坑是必须提前知道的。不管你是刚接触西门子 PLC 数据块的新手还是已经写了几年 FB 但总觉得结构不够清爽的老手看完应该都能对号入座找到自己项目里能立刻改的那一处。1. 从一个多台同型号设备的编程困境说起我印象最深的一次是某条包装线上有六台同型号的伺服驱动控制逻辑完全一致只是各自的 I/O 点和参数不同。当时接手的是别人写的程序打开一看六个 FB 块内容一模一样只是名字后面加了 _1 到 _6每个块各自带一个背景 DB。代码量倒是能忍问题出在后面加功能的时候——工艺那边要求增加一个累计运行时间超过阈值报警的逻辑。这意味着六份代码要改六遍改完还得一处处核对生怕哪一份漏了某行。那次之后我对实例这块就特别上心。其实这三种实例方式的本质都在回答同一个问题同一个 FB 的逻辑要复用但数据必须分开存这个分开存的位置和方式到底有几种选法。1.1 逻辑与数据分离FB 和 DB 到底是什么关系先把最基础的一层说透。FB 是带记忆的逻辑块它自己不带数据存储空间只带一份数据结构的定义真正存数据的是背景 DB。你可以把 FB 理解成一张表格模板背景 DB 就是按这张模板打印出来的一张张实表。模板只有一份实表可以有很多张每张表里填的内容互不影响。这层关系决定了三件事。第一FB 里 VAR 区声明的变量也就是 Static 静态变量在每一个背景 DB 里都有一份独立的副本哪怕变量名一模一样。第二FB 里的 Temp 临时变量不进入背景 DB它只存在于扫描周期的调用栈里调用结束就没了所以不能用 Temp 存需要跨周期保持的状态。第三Input 和 Output 参数也是背景 DB 的一部分它们的值在调用时被写入、被读出跟着 DB 一起保存。很多人栽在第二点上。比如写了一个带上升沿检测的 FB把边沿记忆位声明在 Temp 里结果发现边沿检测完全失效——因为每个扫描周期 Temp 都被重新分配记忆位永远是初始值。这类问题的根子不在指令用得对不对而在没搞清楚哪些数据真正落进了 DB 里。1.2 三种实例形态解决的是三个不同层次的问题再往上走一层看这三种实例其实对应三种不同的复用粒度。单个实例是最细的粒度一个 FB 的每一次调用配一个独立的背景 DB数据之间彻底隔离代价是 DB 数量随调用次数线性增长。它适合每个实例都要被外部单独访问、单独调试、单独下载的场合。多重实例是把粒度收拢在父 FB 的静态区里声明子 FB 类型的变量子 FB 的实例数据就直接嵌在父 FB 的背景 DB 里不再单独占一个 DB 号。它适合子部件只服务于当前父对象、外面不需要单独碰它的结构比如一台设备 FB 内部的几个阀门 FB、几个轴 FB。参数实例则是把粒度做成可替换父 FB 的接口里留一个类型为某个 FB 的 InOut 参数调用的时候由外部把某个现成的背景 DB 塞进来。它适合调度者与被调度者要解耦的场合比如一个统一的设备管理器今天管电机、明天管阀门逻辑不变只换传进来的实例。这三者的差别不是风格偏好而是直接决定了你的程序在后期维护、在线修改、故障排查时的难易程度。下面挨个拆。2. 单个实例最直白也最容易被滥用的那种写法单个实例是所有教科书第一个讲的用法也是绝大多数人最先掌握的。它的写法很直接在 OB 或者其他 FB 里调用一个 FB 时系统会提示你分配一个背景 DB你可以让它自动生成比如 DB1、DB2……也可以自己指定编号和符号名。从内存角度看每个单个实例 DB 都是完全独立的一块存储区。同一型号的十台设备就是十块互不相干的存储区各自的 Static 变量、Input、Output 都在这块区域里按顺序排列。这种独立性带来的是最直观的可控性你可以在线打开任何一个背景 DB看到里面每个变量的当前值你可以单独把某一个背景 DB 下载下去而不影响其他你也可以用交叉引用精确查到某个实例的数据在哪些地方被读写。2.1 背景 DB 里到底存了哪些东西打开一个背景 DB看到的变量列表和 FB 接口区的结构是一一对应的但有几点容易被忽略。第一Input 和 Output 参数确实存在 DB 里。它们在 DB 里占位置调用时写入、返回时读出。所以如果某个 Input 参数你从来没在调用处赋值它在 DB 里就是一个未初始化或者保持上次值的状态不会自动清零。这个特性有时候能当保持型参数用有时候就是隐患。第二InOut 参数在 DB 里不存实体数据。InOut 本质是个引用DB 里存的是一个指向实际数据的指针信息真正的数据在你传进来的那个变量所在的地方。这一点非常关键后面讲参数实例时还会再提到——正是因为 InOut 是引用才有了参数实例这种玩法。第三Temp 变量完全不进 DB。所以背景 DB 的大小只跟 Static、Input、Output 有关跟 Temp 无关。第四S7-1200/1500 默认启用优化的块访问背景 DB 的变量没有绝对地址只能符号访问如果取消这个选项就能看到每个变量的偏移量。偏移量在实际计算 DB 大小时很有用尤其是在你需要评估一块 DB 会不会超出某个限制的时候。我习惯的做法是新建 FB 之后先关心 Static 区里放了什么因为那块才是真正决定 DB 体积的部分Input 和 Output 尽量精简能用 InOut 传引用的就别用 Input 拷一份。2.2 同一个 FB 多次调用时单个实例的两种命运单个实例最典型的使用方式是一设备一 DB。六台伺服就是六个背景 DB各自指向自己的 I/O。这种写法在设备数量固定、且每台设备都需要独立调试的场合是合理的甚至是最优的。但问题出在设备数量不固定的时候。比如一个项目里输送线工位数量是按客户需求配置的可能是 4 个也可能是 12 个。这时候单个实例就尴尬了你得预先想好最大数量把所有背景 DB 都建出来调用的时候用条件判断跳过不存在的工位。程序里到处是IF StationIndex StationCount THEN这样的判断可读性直线下降。更麻烦的是那些没启用的背景 DB 依然占着内存虽然不至于影响运行但会让整个项目看起来乱七八糟。另一种命运是一个 FB 只有一个实例反复重入。这种做法在逻辑块级别是可行的但一定要非常小心如果同一次扫描里对同一个背景 DB 调用了两次第二次调用会覆盖第一次留下的 Static 状态诸如边沿记忆、状态机步序号、累计计时这类变量全都会乱。我见过一个真实的案例某工位程序里为了省 DB把同一个 FB 在同一周期里调了两次一次用于手动模式一次用于自动模式结果运行起来两个模式的内部状态互相打架手动切自动时电机会出现短暂的双向输出。排查了很久才定位到是背景 DB 共用导致的。所以有一条判断标准很实用只要同一个 FB 需要在同一扫描周期内被调用多次且各次调用需要独立的内部状态就绝对不能用同一个背景 DB。2.3 什么时候坚决用单个实例说完问题也得说清楚它不可替代的地方。需要 HMI 直接访问的数据用单个实例最省事。HMI 画面要读某个设备的运行小时数、故障代码、当前配方号这些变量如果藏在多重实例里HMI 就得写一串嵌套路径比如设备DB.子部件.变量虽然也能写但维护起来容易出错。而单个实例的背景 DB 是顶层 DBHMI 直接引用 DB 名加变量名就行。需要在调试时单独在线修改、单独下载的也用单个实例。比如现场调试某台设备你想只把它一个背景 DB 的值改掉不影响别人单个实例是最直接的。还有一种是全局唯一的服务型 FB比如系统级的报警汇总、时间同步、配方切换整个项目只有一份那用单个实例就是最自然的不需要多想。3. 多重实例把子 FB 的内脏藏进父 FB 的 DB 里多重实例的写法第一次见到的人往往会觉得有点玄在 FB 的 Static 区里声明一个变量数据类型不是 Int、不是 Real而是另一个 FB 的名字。编译之后那个子 FB 的实例数据就悄无声息地嵌进了父 FB 的背景 DB 里一个独立的 DB 号都不会多出来。我第一次用这个功能是在做一个多轴控制的项目。一台设备带四个轴每个轴的控制 FB 逻辑完全一样。如果每轴一个背景 DB就是四个 DB加上设备主 FB 自己的一共五个。后来改成在设备主 FB 里声明四个轴 FB 类型的静态变量背景 DB 一下就变成一个——所有的轴数据都在那一个 DB 里按顺序排好。这个改动的收益不只是少几个 DB。更大的好处是结构上的封装这台设备的所有数据包括它内部每个轴的数据全部装在一个 DB 里逻辑上的边界和存储上的边界一致了。你要拷贝这套设备逻辑给另一台同类设备只需要复制一个父 FB 的调用和一个背景 DB不用担心漏拷哪个子 DB。3.1 静态区声明 FB 类型变量的本质从存储角度看多重实例做的事情非常简单把子 FB 的背景 DB 内容原封不动地搬到父 FB 背景 DB 的某个偏移位置上。父 FB 的静态区里声明了MotorRoller : FB_Motor编译之后父 DB 里就会划出与FB_Motor 背景 DB 应占大小相同的一块区域这块区域的起始偏移由编译器根据前面的变量和 S7 的对齐规则算出来。子 FB 里面的 Static、Input、Output 全在这块区域里。子 FB 的 Temp 依然不进 DB。正因为是同一块存储区所以访问速度上多重实例其实比单个实例还略快一点——不需要通过 DB 号去寻址直接在父 DB 的偏移上操作。当然这个差异在实际项目里可以忽略不计S7-1500 的访问速度已经足够快。写法上多重实例的调用和普通 FB 调用几乎一样直接写实例名加参数FUNCTION_BLOCK FB_Conveyor VAR MotorRoller : FB_Motor; MotorBelt : FB_Motor; END_VAR BEGIN #MotorRoller(Start : #CmdRoller, Stop : #CmdRollerStop); #MotorBelt(Start : #CmdBelt, Stop : #CmdBeltStop); END_FUNCTION_BLOCK编译之后FB_Conveyor的每一个背景 DB 里都包含两份FB_Motor的实例数据。如果FB_Conveyor本身被调用了三次三台输送机那就是三块 DB每块 DB 里各含两份电机数据一共六份电机数据——但 DB 总数只有三个。这个数量关系一定要在脑子里过清楚因为它直接影响到后面的选型判断。3.2 编译后的存储布局怎么去看多重实例最让人心里没底的地方是看不见。打开父 FB 的背景 DB你看到的是一个个变量名比如MotorRoller.Start、MotorRoller.RunCmd但你看不到它们各自的绝对地址也不知道这块数据到底占了多大。想看的话有两个办法。一个是取消背景 DB 的优化的块访问属性改回标准访问这时候变量列表里就会出现偏移量、数据类型、重复因子这些列你可以清楚看到MotorRoller从某个偏移开始占了连续的一大段。另一个办法是在 DB 的编辑界面里展开嵌套结构优化的块访问下也能一层层展开看。我建议在做完多重实例的改造之后专门花时间看一眼 DB 的总大小。有一次我把一个配方相关的 FB 嵌进了设备主 FB结果那个配方 FB 里有一个大的数组父 DB 一下从 2KB 涨到 30 多 KB。虽然功能上没影响但每次下载都要多等好几秒而且在线监控打开也慢了。后来我把那个数组改成用全局 DB 加指针索引访问父 DB 才瘦回去。顺便提一句 S7 的对齐规则Bool 变量会按位打包但一旦类型发生变化比如从 Bool 跳到 Int编译器会重新对齐到字或者双字边界中间可能产生填充字节。所以变量的声明顺序会实实在在影响 DB 大小。把同类型的变量集中声明往往能省下不少空间尤其是声明了大量结构体或者多重实例的时候。3.3 多重实例的三条硬约束用多重实例之前必须清楚它的限制不然容易在后期撞墙。第一条子实例的数据外部不能直接访问。这是封装性的另一面。父 FB 外面想读MotorRoller.RunCmd是读不到的因为父 FB 的接口里并没有暴露这个变量。要想读必须在父 FB 的接口里加一个 Output或者加一个 InOut 参数把值引出去。这个限制会逼着你把接口设计得更克制但短期看确实麻烦。有些项目里为了图省事把子变量一个个往上抄成 Output结果父 FB 的接口变得极其臃肿反而破坏了封装的意义。第二条同一个多重实例不能被两个父 FB 共享。因为它的数据实体就在父 DB 里面天然属于那一个父 DB。如果两个地方需要操作同一份数据那只能用参数实例的方式把同一个背景 DB 传给两边。第三条调用深度是有限的。S7 系列对块嵌套深度有明确上限多重实例多层嵌套会占用调用栈。虽然一般项目到不了这个深度但如果你的结构是系统 FB 嵌设备 FB、设备 FB 嵌工位 FB、工位 FB 嵌轴 FB这种四五层的递归式嵌套就要算一算了。我的经验是嵌套三层以内最舒服超过三层可读性会急剧下降调试时想找一个具体变量要一层层点进去很耗时间。还有一条不算硬约束但很要命的在线修改父 FB 的接口会导致整个父 DB 重新初始化。因为接口一变DB 的布局就变了里面所有多重实例的数据区跟着挪位置。这意味着你为了加一个输出变量可能把整个设备的所有运行状态全清掉。所以在设计父 FB 接口时宁可一开始多预留几个备用变量也别想着后期随时加。4. 参数实例让同一段逻辑在不同数据上跑起来如果多重实例是把子部件的内脏藏起来那参数实例就是反过来的思路——把选择权交给调用者。父 FB 不自己持有实例数据而是在接口里开一个口子声明我需要一个某类型的 FB 实例调用的时候由外面把实例塞进来。接口声明上这个口子开在 InOut 区数据类型选择某个 FB 的名字。这是 TIA Portal 里一个不太起眼但很有用的能力FB 的名字本身就是一种数据类型可以像 Int、Real 一样拿来声明变量。FUNCTION_BLOCK FB_MotorManager VAR_IN_OUT ioMotor : FB_Motor; END_VAR VAR_INPUT StartCmd : Bool; StopCmd : Bool; END_VAR BEGIN #ioMotor(Start : #StartCmd, Stop : #StopCmd); END_FUNCTION_BLOCK调用的时候把现有的实例传进去DB_MotorManager_1(ioMotor : DB_Motor_1, StartCmd : ..., StopCmd : ...);这里DB_Motor_1可以是一个单个实例背景 DB也可以是某个父 FB 里的多重实例变量。两种情况在语法上都成立区别在数据实体所在的位置不同。4.1 参数实例真正的价值在解耦单看上面的例子参数实例好像没什么必要——既然我就在这个 OB 里调用直接把电机 FB 调了不就完了何必绕一层。它的价值要到调度者需要统一处理一批不同类型对象的时候才显现。比如你有一套设备管理器它要统一负责所有设备的启停确认、故障汇总、运行时间累计。这些逻辑对所有设备都是一样的但设备的具体控制逻辑各不相同——电机有电机的逻辑阀门有阀门的逻辑加热器有加热器的逻辑。如果没有参数实例你有两条路一是给每种设备写一个管理器 FB逻辑重复 N 份二是在管理器里用 IF 判断设备类型然后分别调用代码里塞满分支。有了参数实例你可以定义一个设备基类的接口约定在 S7 里没有真正的继承但可以靠接口一致来模拟让所有设备 FB 都实现同样的 Input/Output 接口然后管理器里用参数实例统一调用FUNCTION_BLOCK FB_DeviceManager VAR_IN_OUT ioDevice : FB_DeviceBase; // 所有设备 FB 的接口保持一致的约定 END_VAR VAR RunHours : DInt; FaultLatch : Bool; END_VAR BEGIN #ioDevice(Enable : #EnableCmd, Fault #FaultLatch); END_FUNCTION_BLOCK这种写法的好处是管理器 FB 只写一次以后新增设备类型只要接口对得上管理器这边一行都不用改。顺带说一句这种靠接口一致来模拟多态的做法在 PLC 里其实挺常见。它没有面向对象语言里的虚函数表那么灵活但胜在简单直白编译期就能检查出接口不匹配的问题运行时不会出现调错对象的情况。4.2 参数实例和多重实例的存储差异在哪这一点值得单独说清楚因为它决定了两者的性能和维护成本。多重实例的数据实体在调用者自己的 DB 里是一块固定的偏移区域访问路径最短。参数实例的数据实体在传进来的那个实例里调用者拿到的只是一个引用。运行的时候程序要通过这个引用去找真正的数据位置相当于多了一次间接寻址。这个开销在实际项目里通常感觉不到S7-1500 处理这点开销毫无压力。但维护成本上的差异就明显了。多重实例的关系是编译期固定的父 FB 里写了几个子 FB 变量就永远是那几个数量不能变。参数实例的关系是调用期决定的同一个管理器 FB 字面上只有一个调用点但实际上可以传不同的实例进去管十个设备和管一百个设备代码一模一样。这个差异直接对应到前面说的设备数量不固定的场景。数量固定的多重实例更简单数量可变的参数实例更合适。还有一个容易被忽略的点参数实例可以把一个多重实例再传出去。也就是说父 FB 里声明了一个子 FB 的多重实例然后调用另一个 FB 时把这个多重实例作为实参传给它。这时候数据实体仍然在父 DB 里但操作权临时交给了另一个 FB。这个用法在分层设计里很好用比如设备 FB 里持有轴的实例但把轴的实例传给一个通用的轴调试FB 去做参数计算算完再还回来。4.3 现场最容易踩的时序坑参数实例的灵活性带来的代价是调用者必须保证传入的实例是有效的、且没有被别处同时操作。最常见的一种错法是同一个背景 DB 被传给了两个不同的调度 FB两个调度 FB 在不同的 OB 里跑比如一个在循环 OB一个在中断 OB。中断 OB 抢占循环 OB 的时候如果两边都在改同一份实例数据就会出现数据撕裂。表现是偶发的、莫名其妙的参数跳变很难复现也很难查。还有一种错法是传了一个还没初始化的实例。理论上背景 DB 在首次扫描时会按 FB 里声明的初始值初始化但如果你传的是一个条件生成的实例或者在启动 OB 里还没执行到就用了就可能读到意料之外的值。稳妥的做法是在启动 OB 里对所有关键实例做一次显式初始化。另外要提醒一点参数实例的接口一旦定下来调用处的实参类型就必须完全匹配。你声明的是FB_Motor类型调用的时候传入一个FB_Valve类型的实例编译器会直接报错不会偷偷转换。这其实是好事能在编译期挡住大部分误用。但也意味着如果你想让管理器同时管电机和阀门就得让它们的接口长得一模一样或者用不同的管理器 FB。5. 选型决策三种方式对照与取舍讲到这里三种方式各自的脾性基本清楚了。落到实际项目上怎么选我的习惯是先看两个问题这个实例的数据外部需不需要单独访问这个 FB 的调用数量是固定的还是可变的这两个问题的答案基本就能定位到该用哪种。5.1 一张对照表看清内存、耦合与复用对比维度单个实例多重实例参数实例数据实体位置独立背景 DB父 FB 的背景 DB 内传入的实例所在处DB 数量每次调用一个不额外增加不额外增加外部可访问性直接符号访问只能通过父 FB 接口取决于传入的实例能否跨对象共享可以传 DB 名不能可以传同一实例数量可变性需预先建好编译期固定调用期灵活在线修改影响面仅本 DB整个父 DB取决于传入的实例接口耦合度低高父接口含子中靠接口约定典型场景全局服务、HMI 直连设备-部件结构统一调度、批量对象这张表里最值得琢磨的是在线修改影响面这一行。单个实例改接口只影响自己一个 DB多重实例改父 FB 接口整个父 DB 连同里面所有子实例一起重置参数实例的影响面取决于你传的是谁如果传的是顶层 DB那就等于改了那个 DB。现场调试阶段这个差异会被放大。想象一下设备已经带料运行你要加一个报警输出。如果这个变量在单个实例的 DB 里改完只下载那一块其他设备不受影响。如果它在一个装着二十个子实例的父 DB 里改完整个父 DB 重置二十个子部件的状态全归零可能触发现场的连锁停机。这就是为什么我在老项目上做重构时动多重实例之前一定要先确认现场状态。5.2 按项目规模和团队习惯做选择抛开技术细节还有一层是人的因素。小项目、一两个人写、逻辑不复杂我倾向于尽量用单个实例。原因很朴素看得见、找得到、改得快。项目树里一排 DB点开哪个就是哪个不用在脑子里维护一张嵌套表。这种项目里谈封装、谈架构收益远不如把逻辑写清楚来得实在。中型项目、三到五个人协作、有明确的模块划分多重实例的价值就出来了。每个人负责一个设备模块各自的子部件封装在自己的父 FB 里互不干扰。接口边界清晰别人不需要知道你内部用了几个阀门、几个轴只需要知道你暴露出来的那几个输入输出。合并代码的时候冲突也少。大型项目、设备类型多、需要统一调度参数实例就更合适。特别是那种一台主控带几十个从站设备、每种设备的逻辑不同但管理流程一致的结构参数实例几乎是唯一能让代码不爆炸的方案。还有一种情况是混合使用。我现在的习惯是顶层用单个实例中间层用多重实例跨模块的调用用参数实例。具体说每台设备的入口 FB 给它一个独立的背景 DB设备内部的各个功能块用多重实例嵌进去设备之间需要互相操作的地方用参数实例把对方的实例传过来。这个分层方式不是拍脑袋定的它对应的是访问频率和变更频率两个维度。顶层 DB 被 HMI 和外部程序访问得最多变更也最频繁所以给它独立性中间层相对来说稳定嵌套起来省 DB 号跨模块的调用需要解耦所以用引用。5.3 重构老项目时的注意事项如果你接手了一个背景 DB 满天飞的项目想把多重实例引进来有几个地方要提前想好。第一步是梳理调用关系。把每个 FB 的调用点列出来标注它现在用的是哪个背景 DB然后按设备或者按功能归类。归类之后你会发现很多背景 DB 其实只被一个地方调用天然适合合并成多重实例。第二步是估算合并后的 DB 大小。别只看变量个数要看实际字节数。有数组、有字符串、有嵌套结构体的很容易算漏。合并前先把每个子 FB 的背景 DB 大小记下来加起来再对比一下 S7 对 DB 大小的限制不同型号有差异通常几百 KB 到几 MB确认不会超标。第三步是分批改别一次全改。先挑一个独立的、影响面小的模块试点改完下载到测试环境跑一遍重点看在线监控能不能正常展开、HMI 变量有没有断链、报警有没有延迟。确认没问题再推进下一个模块。改的时候有个细节特别容易出问题原来通过绝对地址访问的 HMI 变量改成多重实例后路径会变。如果 HMI 那边用的是绝对地址或者旧的符号名就会读不到数据。稳妥做法是在 PLC 侧先把要暴露给 HMI 的变量汇总到一个单独的输出结构里HMI 只认这个结构底层怎么改都不影响。6. 拿一个小 Demo 把三种方式跑通并对比光说不练容易虚我建议每个接触这块的人都自己搭一遍。下面这个 Demo 不复杂但能把三种方式的关键差异都体现出来。6.1 Demo 的结构设计设计一台输送机设备内部有两个电机一个驱动滚筒一个驱动皮带。要求每个电机有独立的启停控制和运行小时累计并且能单独从 HMI 上读到运行小时。然后做三个版本。版本 A 全部用单个实例电机 FB 调两次两个独立背景 DB输送机 FB 再给一个独立背景 DB。版本 B 用多重实例输送机 FB 内部声明两个电机 FB 类型的静态变量只给输送机 FB 一个背景 DB。版本 C 用参数实例写一个带 InOut 参数的电机管理器FB调用时把不同电机实例传进去。三个版本功能完全一样只是存储结构不同。搭完之后做三件事。6.2 对比 DB 数量和实际占用先看项目树里生成了几个背景 DB。版本 A 是三个两个电机加一个输送机版本 B 是一个版本 C 是三个或者更多取决于你给多少电机分配了独立实例。再看每个 DB 的实际大小。在 TIA Portal 里右键 DB 看属性或者在线的时候看存储占用。版本 B 的那个 DB 大小应该约等于版本 A 三个 DB 之和可能还会略大一点因为有对齐填充。这一点很能说明问题多重实节省的是 DB 的个数不是总容量。数据总量是守恒的只是从分散变成了集中。这个认知很重要。我见过有人以为用了多重实例能省内存结果发现主 DB 大得吓人还以为是编译器有问题。其实只是账从多个户头并到了一个户头上。6.3 在线监控与交叉引用验证第三个动作是验证访问方式。版本 A 里直接在背景 DB 里就能看到两个电机各自的小时数改起来很方便。版本 B 里要先打开输送机的背景 DB展开MotorRoller这个结构再找到里面的RunHours。改是可以改但每次都要点进两层变量多了会有点烦。而且如果你想把这两个小时数送到 HMI得在输送机 FB 的接口上加输出变量。版本 C 里数据还是在各自的电机实例里管理器只是操作它所以监控方式和版本 A 差不多只是要知道当前传的是哪个实例。做完这一轮再回过头去看自己手头的项目你会很自然地发现哪些地方其实可以合并、哪些地方必须拆开。这种直觉不是看书看出来的是手搭出来的。我个人在这些年里的体会是实例方式的选择本质上是在独立性和结构性之间找平衡。独立性强的方案好调试、好单独修改但项目一大就散结构性强的方案清晰、省 DB 号但一改动就是一大片。真正成熟的做法不是死守某一种而是在项目初期就把层级想清楚顶层留足独立的余地内部该收拢的收拢跨模块的地方用引用解耦。做完这一步后面的维护工作量能差出好几倍。还有一个很实用的小习惯每次新建 FB 之前先问自己一句这个东西需不需要被外面单独看见。答案是否定的就考虑往多重实例里放答案是肯定的就给它独立的 DB。这条规则简单粗暴但在我经手的项目里命中率相当高。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →