[西门子] 当IT工程师第一次写PLC代码

[复制链接]
查看62 | 回复0 | 2024-5-22 07:36:50 | 显示全部楼层 |阅读模式
oh my god,PLC代码这是什么代码!?给我整不会了。

三年前我从IT咨询公司到了做自动化的公司,虽然做的还是软件相关的业务,但是还是得学一下自动化代码是怎么写的。我本科学的机械设计自动化,研究生学的机械电子工程,虽然我隐约知道PLC这个东西,但完全没碰过。隔壁做自动化的同学应该是学过这玩意儿。我2007年读研究生的时候做过一个基于Windows系统的PCB钻铣床控制系统,在工控机上用C#写了整套控制程序,用运动控制卡/IO卡完成和底层的运动控制,也算是实现了一个自动控制系统。对于我已经有一些控制经验的人,初学PLC编程也是困难重重:

迷惑之一:异步操作?

当三年前第一次写PLC程序的时候,给我整懵了。梯形图?电路图?跟着教程来个点个灯泡的程序还是算是简单,后来想弄个延迟几秒钟后熄灭,就彻底懵了。本来一个sleep就搞定的事情,为什么PLC就没这东西呢?

图:网上随便找了个图,感受一下什么是梯形图,怎么编程就成了画电路图了?



其实这里有一个非常大的问题就是PLC的执行方式和IT写代码的方式是完全不一样的。PLC在设计上是实时控制的,需要在确定的时间内(例如一定在10ms)执行完一段程序。所以如果有一个sleep操作的话,PLC整个程序就停了下来,所有的控制、通讯就中断了,所以PLC程序只能分时间片来运行。

写IT代码的话,目前大部分语言(Python、C#、C++、TypeScript)都是串行执行的,非常符合人的思维方式。但写IT的同学也知道,如果是单线程模型,如果需要做sleep、通讯,只能傻傻的等待,然后程序卡死。如果需要处理sleep、长时间通讯这种情况而不会卡死的话,通常将这些操作放到一个单独的线程中去,主线程继续刷新UI之类的。在现代的高级语言中,还有一种async/await的异步操作,主要还是解决一些需要异步等待的场景(IO、网络请求等),否则会带来一堆的callback,多层的程序的嵌套会非常难受。网上找了两个图,感受下层层递进的callback:





高级语言通过async/await将这种嵌套转换成了相对串行的代码,代码就好看多了,理解起来也容易了。这些代码会返回promise对象,然后一个tick一个tick的执行,这样就避免了单线程的阻塞。

网上找了个图,感受下callback -> promise -> async/await的区别。在async/await没有得到普遍支持那个年代,写异步代码就是个灾难。



所以说了那么多和PLC编程有什么关系?PLC可以看成一个需要异步执行的系统,每个时间片都需要执行一遍程序,如果需要做等待操作的话,就需要一个类似“计数器”一样的东西来记录走了多久了(当前时间-之前的时间),然后计数器满足时间后再执行下面一段。

如果把这种异步编程的经验(callback)转换到PLC编程中,就比较好理解了。比如以下一段ST代码实现CLAMPING_PARTS到PICK_AND_PLACE步骤的转换,中间用到了一个_delayTimer计数器,每个周期会去判断_delayTimer的output是否满足,满足后执行命令,并转换状态。
CASE _status OF  AssemblerStatus#CLAMPING_PARTS:      _delayTimer(TRUE, LT#1s);IF _delayTimer.output THEN          _delayTimer(FALSE);          lidClamp^.Release();          baseClamp^.Release();          twoAxisPickPlace^.Start();          _status := AssemblerStatus#PICK_AND_PLACE;      END_IF;  AssemblerStatus#PICK_AND_PLACE:IF twoAxisPickPlace^.IsDone() THEN                              baseClamp^.Raise();          _status := AssemblerStatus#MOVE_TO_FINISH;      END_IF;
通常在需要分片进行操作时,例如网络请求、串口通讯等,需要将这些任务分成时间片来设计,相应的库会提供一个类似InProgress, Done这样的一些引脚来告诉你执行的状态。当状态满足后再做下一步。

整个PLC程序可以理解成一个大的while循环,永不退出。

迷惑之二:怎么触发一个操作?

第一次接触FB的时候会发现这个东西很奇怪,全身上下长满了“引脚”

感受一下一个简单的Pump的FB



还有双向执行器的例子



作为IT工程师很难理解搞一堆这些参数是干啥的呢?平时不应该是motor.start()就可以启动电机了,或者actuator.goHome()就可以控制回原点么?这个FB怎么调用??

理解这个问题嘛,其实是现在的Function或者Function Block(可以理解为能保存状态的Function,很神奇吧)都是在模拟电路的行为,PLC最初就是从电路来的,所以在梯形图里面或者FBD等展示的时候,还是都以电路的形式展现的,而电路中不存在什么函数调用,只存在高电平低电平等。物理世界中的电路是一个连续的时间,在PLC中模拟这种电路的行为需要切时间片,然后再模拟电路的行为。

想通了这个就好理解为什么这玩意儿那么奇葩了。

如果IT工程师想调用类似于Motor.Start()这种方式的话,在目前的博途中是暂时做不到的,只能通过引脚设置true这种方式来“激活”特定的行为。这些IT的思考方式,在西门子的SIMATIC AX中将可以利用完全面向对象的方式来实现了。例如下面这段代码就是FactoryIO中的一个简单的机构的例子,可以将需要处理的工件对齐。



CLASSClamp IMPLEMENTS ICyclicVAR_clampCmd : BOOL;_raiseCmd: BOOL;END_VAR
VARPUBLICClampedSignal: IBoolInput;ClampSignal: IBoolOutput;RaiseSignal: IBoolOutput;END_VAR
METHODIsClamped : BOOLIsClamped := ClampedSignal.GetValue();END_METHOD
METHODPUBLIC DoClamp_clampCmd := TRUE;END_METHOD
METHODPUBLIC Release_clampCmd := FALSE;END_METHOD
METHODPUBLIC Raise_raiseCmd := TRUE;END_METHOD
METHODPUBLIC Down        _raiseCmd := FALSE;END_METHOD
METHODPUBLIC Reset_raiseCmd := FALSE;_clampCmd := FALSE;END_METHOD
METHODPUBLIC CycleClampSignal.SetValue(_clampCmd);RaiseSignal.SetValue(_raiseCmd);END_METHODEND_CLASS
在调用的时候,通过操作对象的方法即可实现控制:
//控制baseClamp.Clamp();baseClamp.Release();//在程序中实现循环扫描baseClamp.Cycle();
这种方式基本上解决了IT程序员比较难理解FB这些引脚的问题,也变成了真正好理解的OO方式了。唯一可能需要多考虑的就是程序是以时间片扫描形式执行的,需要做一点点的特殊处理而已,这将在后续讲到。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册哦

x
您需要登录后才可以回帖 登录 | 注册哦

本版积分规则