2016年4月18日

浅谈MES的通用设计之:工艺参数的下载


MES是计划和执行层之间的桥梁,除了将工单下发到车间外,还有一个重要功能是将工艺参数下载到设备,从而起到指导现场作业的作用。
本文试以实例说明常见的两种设计思路,以及一种更为通用的设计方法。

业务场景及设计实例1
某发动机工厂支持混线生产,有两种发动机,排量分别为2L、3L。当发动机到达加油机工位时,PLC需要判断发动机的排量,从而进行对应的加油作业,如2L发动机加20L油,3L发动机加30L油。
实现方法如下:
为此工位专门写一个程序,逻辑步骤为:
1、发动机到达时,PLC读取RFID存储的发动机序列号。
2、PLC将发动机序列号上传到MES服务器,执行查询请求。
3、MES根据发动机序列号查询得到工单号。
4、MES根据工单号查询得到制造BOM。
5、MES根据油缸料号的命名规则,查询得到发动机对应的油缸类型。
6、MES将油缸类型数据下载到PLC。
7、PLC根据油缸类型决定加油量,进行加油作业。

业务场景及设计实例2
某整车厂焊装车间支持混线生产,同一款车有两种车门配置,分别为4门、5门。当车身到达某焊装工位时,PLC需要判断车门配置,从而决定焊接机器人的取料路径。
实现方法如下:
扩展工单的属性表,为每个工单建立一个长长的配置单,包括车型、门数、天窗、颜色、内饰等所有工艺需要的配置参数。逻辑步骤为:
1、车身到达时,PLC读取RFID存储的车辆识别码。
2、PLC将车辆识别码上传到MES服务器,执行查询请求。
3、MES根据车辆识别码查询得到工单号。
4、MES根据工单号关联查询配置单,得到所有配置参数。
5、MES将所有配置参数下载到PLC。
6、PLC读取指定的数据块位置,得到门数参数。
7、PLC根据门数参数决定取料路径。

方法1的缺点是几乎每个工位都要做客户化开发。
方法2的缺点是配置单的准备相当繁琐,并且下载到PLC的数据量相当大。

下面介绍一种更为通用的、基于工艺配方的脚本化设计方法。
1、首先提供一个界面,允许用户定义工艺配方。
决定工艺配方唯一性的条件有:产品料号、工位号、步骤号、工单执行时间等。
每个工艺配方允许用户定义解释性字段,如名称、说明、图片等。
每个工艺配方的输出值为不定长的字符串。
为每个工艺配方定义字符转换条件,如长度、空值处理等。
每个工艺配方对应的逻辑是一个数据库函数。
也就是说,每次查询一次工艺配方,相当于运行一次数据库函数,根据输入值及函数逻辑(通常是一次查询),得到输出值也就是工艺配方的值。
2、然后编写一段代码,实现:
1) 运行工艺配方,得到每个步骤对应的初始工艺参数。
2) 根据字符转换条件,得到更为规范的步骤工艺参数。
3) 将工位所有步骤号对应的步骤工艺参数,根据步骤顺序进行拼合,得到工位完整的工艺参数。
3、然后将完整工艺参数下载到PLC。

工艺配方的示例:


表中,字段itemtype表示工艺配方的类型,其中类型0表示常量,工艺参数值为字段itemvalue的值;类型1表示变量,工艺参数值为字段itemfunction的值对应的数据库函数的执行结果。
字段itemlength表示步骤工艺参数的长度。
字段fillchar表示查询返回空值时的填充字符。

Oracle中利用动态游标技术拼合步骤工艺参数的方法示例,下面为部分代码:
  DECLARE
    tmp VARCHAR2(2000);
    tmp2 VARCHAR2(2000);
    tmpfun VARCHAR2(2000);
    allstr VARCHAR2(2000);
    type curtype is ref cursor;
      mycur2 curtype;
    CURSOR mycur IS
    SELECT
      t.itemno,
      t.itemname,
      t.itemtype,
      t.itemvalue,
      t.itemfunction,
      t.itemlength,
      t.fillchar
    FROM t -- 此处t应为工艺配方表
    WHERE t.station = I_STATION -- 此处可添加更多的筛选条件
    ORDER BY t.itemno;
  
  BEGIN

  -- 1. get all item for station
    tmp := '';
    tmpfun := '';
    allstr := '';
        
    FOR rec IN mycur
    LOOP
              IF rec.itemtype = 0
        THEN tmp2 := rec.itemvalue; --> get value
        ELSE --> get function
          tmpfun := rec.ITEMFUNCTION;   
          tmpfun := tmpfun || '(''' || TO_CHAR(I_SN) || ''')';
          tmpfun := 'SELECT ' || tmpfun || ' myvalue FROM dual'; 
          OPEN   mycur2   FOR   tmpfun;  --> get item function
          FETCH   mycur2 INTO tmp2;
          close mycur2;              
        END IF;
        tmp := LPAD(tmp2, rec.itemlength, rec.fillchar);
        allstr := allstr || tmp;          
    END LOOP;         
   
  RETURN allstr;




2016年3月29日

MES中的松耦合设计一例

 

在不少领域,MES深入地参与现场作业,如将防错、配方等指令下发给工位和设备PLC,以指导现场作业。

通常这种MES与设备PLC的交互是实时通过OPC进行的,并且伴随不少的握手互锁逻辑。

但是在一些工厂,业务部门出于对IT系统的不信任,明确要求在MES服务器宕机的情况下不影响现场作业,从而实现所谓的松耦合设计。

以发动机拧紧防错为例,通常拧紧防错的方法是在MES里配置的,比如将机型在各工位的防错方法分配到拧紧枪号,并定义方向、次数、套筒号、扭矩等参数,但是拧紧的实现操作是由工位PLC配合拧紧枪控制器实现的。

因此对于工位PLC来说,有两种执行防错的方式:

方式一:每次当发动机到达拧紧工位时,工位PLC从MES下载拧紧防错参数,然后执行拧紧作业,此谓与MES的紧耦合设计,依赖与MES的实时通讯。

方式二:每次当发动机到达拧紧工位时,工位PLC从MES之外的某个介质获取拧紧防错参数,然后执行拧紧作业,此谓与MES的松耦合设计,不需要与MES进行实时通讯。

方式二的介质主要有两种:RFID与条码。

如下图所示,防错配置数据通过MES服务器>>OPC服务器>>IT PLC>>工位PLC>>RFID进行传递,在上线工位由工位PLC写到RFID上,并可在实际上线作业发生之前提前下载并缓存到IT PLC上,从而实现上线工位的松耦合。

当发动机到达装配工位时,工位PLC从RFID中提取防错配置数据,然后与拧紧枪控制器一起执行拧紧作业,因此拧紧作业发生时也不需要与MES进行实时交互。

利用条码进行防错也非常类似,在上线工位为每个拧紧工位打印一张拧紧参数条码,然后张贴到随车单上,当发动机到达拧紧工位时,操作工扫描对应工位的条码,即可将拧紧参数传递给工位PLC。

 

前面提到的IT PLC可以起到一个关键的数据缓存作用,即在上线作业实际发生之前,提前将上线所需的工单和防错配方等数据下载到IT PLC,这样在上线时,工位PLC直接从IT PLC获取数据,从而绕开了MES上层应用环境。

此外,IT PLC还可以起到数据上传的缓存作用,即设备PLC将业务数据(如过站记录和追溯数据)同步给IT PLC(工位PLC在普通作业工位将数据写到RFID,然后在同步工位将数据提取出来同步给IT PLC),然后由IT PLC在MES系统可用时同步到MES服务器,这样对于工位PLC而言,它无须与MES服务器进行通讯,更加专注于装配作业本身。

 

 



 

2016年3月17日

MES案例研究3 – 质量门检查


某发动机工厂的总装下线工位成为一个瓶颈工位,平均操作时间近2分钟。
该工位有一些简单的附件装配作业,并且配置了质量门检查。
由于业务逻辑较复杂,首先从数据库的层面进行检查,发现此工位的一个关键查询SQL查询时间较长,用了近86秒。于是先对硬件进行升级,把SGA从4G升到8G,结果查询时间降低了18秒,略有改进,但还不能达到业务设定的节拍时间。
接下来进一步分析业务逻辑。
最终下线质量门是一个综合检查站,要检查:
1)       发动机的工单状态是否正常。
2)       发动机的质量状态是否正常。
3)       发动机是否被质量扣留。
4)       发动机的检测结果是否正常。
5)       发动机是否有未清除缺陷。
6)       发动机的AUDIT结果是否正常。
7)       发动机是否经过所有的关键工位。
8)       发动机是否存在少装或多装零件。
这其中,逻辑最复杂、最消耗资源的是最后一步少装检查。
少装检查的逻辑是:
少装/多装物料 = 应装物料(BOM) – 已装物料 + 实时替代件转换
技术方面的困难主要是因为:
1)       BOM是一个动态的结构,需要根据工单上线时间、结构有效时间、零件有效时间进行动态地计算。
2)       存在分装,并且BOM也有层级结构,这样计算BOM时需要进行递归查询。
为了减少动态计算BOM的时间,从技术上可以采取以下方法:
1)       建立一个视图,字段包含所有总成件、分装件、零件等对应的所有信息,用上线时间作为计算的依据。
2)       建一个物化视图,对应此视图的结构。
3)       定期刷新物化视图,从而提前得到需要的数据。
这个改进有效减少了实时递归查询所消耗的时间。
另一方面,对业务流程进行改进:把该工位拆分为两个工位,第一工位做装配,第二工位做检查,这样就减少了装配作业所占用的时间。
经由上述改进,终于把工位节拍减少到58秒,满足了生产的要求。



2016年3月9日

test

just a test...


 

MES案例研究2 – OPC网络阻塞


  
某工厂使用OPC SERVER作为协议转换层,实现MES SERVER与现场PLC的通讯。
现场有2个车间,约有近20个工位需要和MES SERVER通讯。
等到所有工位都调试通过,系统也上线运行了一段时间之后,突然发现一个很妖的问题:OPC响应会越来越慢,最后造成一些工位完全没有响应。
一开始我们怀疑是网络的问题,于是把两个车间的网络隔离观察,发现问题仍然没有解决。
后来我们怀疑是应用的问题,于是在后台查看OPC CLIENT通讯的日志,依然毫无头绪。
接着我们怀疑是OPC客户端配置的问题,于是把所有工位的OPC配置按照手册重新设置了一遍。
这样下来,我们几乎折腾了半个多月,但是问题依然象幽灵一般存在,搞得业务部门非常不满。
后来我们想到试着去看OPC SERVER的事件日志,因为每次OPC通讯建立连接时,客户端是作为一个远程用户登录到服务器的,所以服务器应该会记录登录验证的信息。于是我们按照这个方向去查,也确实发现了不少登录的日志,但并没有显示IP地址、HOSTNAME等有用的信息。
后来我想到我们是用域帐户作为OPC远程用户进行登录管理的,那么理论上每次登录验证都需要通过域控制器进行。于是赶紧联系域管理员,从域控制器上导出最近一个月的事件日志,然后把此日志经过处理,筛选得到所有指向OPC SERVER的登录验证信息。经过这样处理,果然大有发现:有2台PC异常频繁地请求访问OPC SERVER,于是抄下这2台PC的机器名和IP 地址,到现场了解情况,很快地真相大白:原来这2台PC是用于HMI的,安装了XP操作系统,然后运行WINCC RUNTIME,由于PLC工程师不断地通过U盘拷贝程序和文件,结果感染了病毒,因此这2台PC不断地申请访问OPC SERVER,进而形成类似DDOS攻击的效果,从而造成OPC网络瘫痪。于是我们将此2台PC断网杀毒,车间的OPC网络马上恢复正常运行。
这个案例给我的教训是:
1.       一定要做好生产网络的隔离。首先务必要将生产网络从办公网络隔离,其次不同车间之间最好也划分VLAN进行隔离。
2.       不仅是运行MES客户端的PC要安装杀毒软件,HMI、测试机台等也都要安装杀毒软件,从而防止病毒的入侵。
3.       由于早期版本OPC采用DCOM进行远程通讯,在典型配置时OPC服务器的安全等级非常低,容易被入侵,此时条件允许的话,要考虑配置用域账户管理OPC远程登录,这样可以通过域控制器的事件日志回溯每次的登录请求,方便日后追查分析。



2016年2月24日

MES案例研究1 - 大表并发查询





某工厂是生产数码产品金属外壳的,每天近100万件的产量,随着圣诞节的临近,客户订单大量增加,但是生产却跟不上,经初步分析,发现问题发生在激光雕刻二维码的工位,由于镭雕机从MES取号的时间太长,造成生产的瓶颈。
该厂MES的主要功能是做生产追溯,包括:生产过程记录、关键工位检查、质量问题收集等。
在镭雕工位,客户端程序要从MES中查询得到对应机型的最小序列号,然后传给镭雕机。
查询SQL的核心逻辑为:
SELECT
  MIN(serial_number)
FROM
  t_product_history t
WHERE t.type = ‘PN1’ //产品类型
AND t.status = 1;    //产品状态,1表示尚未镭雕

通过ORACLE AWR可以得到此查询SQL在某个时间段的总执行时间。
此外,我们可以查询得到SQL_TEXT的执行次数。
两者相除,就得到此SQL的平均时间:竟然达到2秒!
更糟糕的是,查看客户端代码后,发现没有做并发处理,当现场二十多台镭雕机同时工作时,工人一旦发现响应慢,就会不断点击手动请求按钮,造成多并发,进而造成查询的执行时间更长。
接下来只有认真地分析t_product_history这个表的业务。
这个表是一个产品的生产历史记录表,整个生产过程中大约要经过近100个采集工位,每件产品经过每个工位时会在此表中新增一条记录,这样一天100万件产品就要新增近1亿条记录,表容量相当地大。
这个表也做了分区处理,是按月分区的,但是查询SQL的WHERE条件(产品类型和状态)并没有利用分区字段,所以系统会从索引中扫描记录,而索引没有做分区,而索引的容量也已经非常巨大了,这样的后果是每次查询都要扫描数百亿条记录,自然效率低了。
其实数据库里还有另一个表t_product_status,用于表示产品的状态,数据容量要小得多,但自从工单下发后,状态值就为1了,一直到完工变成状态2,所以不能从此表中得到尚未镭雕的最小序列号。
在不更改这两表功能的前提下,只有另辟蹊径了。
首先我们分析生产的特性:大批量少品种,每个工单对应一种产品,每个工单的量都非常大,一般都在几万至几十万的量,每个工单下发后,在镭雕工位做连续生产,中间不会跳序列号。
因此,我们可以把镭雕要取的最小序列号抽象成符合条件的工单的一个特征,一个中间量的指针值。
首先建一个工单的扩展属性表t_wo_pointer,定义以下属性:工单号,产品类型,状态,数量,最小序列号,当前指针值,当前序列号。
当工单下发时,在原存储过程中增加一个小的逻辑,即在此扩展表中增加一条记录,将状态置为1表示尚未镭雕,并计算得到最小序列号,并置指针值为0。
在镭雕工位,查询的SQL更改为:
SELECT
  MIN(Current_sn), //当前序列号
  Pointer         //当前指针值
FROM
  t_wo_pointer
WHERE t.type = ‘PN1’ //产品类型
AND t.status = 1;    //产品状态,1表示尚未镭雕

这样就查询得到了符合条件的最小序列号和指针值。
将序列号输出,然后将指针值加1并更新当前序列号,如果指针值=工单数量则将工单的状态改为2。
然后加上并发事务处理。
经过这番处理后,并没有改动业务逻辑,但是查询的时间从原来的2秒变成只有1.8微秒(要查询的表只有100多条记录),当即解决了此处瓶颈,提升了8%的产能,帮助工厂顺利完成了圣诞节的订单。
我从此案例学到的教训是:
1.       大表查询要慎重。
2.       尽可能不要在历史记录表中执行生产现场控制的逻辑。
3.       大表做了分区以后,分区字段不一定会被查询语句利用到,而大表的索引不一定做了分区处理。
4.       连续数字、序列号可以考虑用指针表的方式处理,前提是要做好并发事务处理。