在面板/半导体行业领域,设备状态的管理一般包含以下两个层面:

1,设备状态管理的层级:一般分为3个层级。主设备(EQP)、一级子单元(Unit),二级子单元(SubUnit)。

2,设备状态的定义:RUN(运行)、IDLE(空闲)、DOWN(宕机)、PM(保养)、ENG(调机)、TEST(测试)、JC(换线)等状态。

这两大方面是设备稼动率、OEE等指标计算的基础。行业内的各公司在这两大基础面的定义相差无几。然而,基于设备状态切换历史,进而计算设备的各项稼动指标的具体逻辑,则不尽然相同。

考虑多腔室类型的设备,借用编程的术语,这些设备具备“并发”能力。举例来说,某设备EQP_MAIN具有两个制程能力相同的UNIT,分别为UNIT1和UNIT2。若UNIT1为DOWN且UNIT2为RUN,此状态持续1小时,此时,无论EQP_MAIN挂RUN还是DOWN,均需要考虑:这1小时并非完整的DOWN或者RUN,而应该折算成RUN 0.5小时和DOWN 0.5小时。

现实世界中的设备层级要远复杂于此例子。但借鉴此思想,对各设备一级单元(Unit)或者二级子单元(SubUnit)进行合理划分,然后考虑不同UNIT状态的组合,并按其对设备状态影响程度,赋予不同的权重因子,则可实现对设备状态精细化的计算。

本文接下来主要描述如何设计算法和程序,来实现这种计算。

首先考虑以下几个要素:

计算时间点的选择:有两种选择,一是预先算好,例如采用ETL的方式。二是实时查询并计算。由于用户要求的应用场景等多种因素限制(例如PM等状态是人为标注的,且存在多次修改备注信息的场景,所以预先算好的方式不适用),本文采用了实时计算的方式。

查询性能的预估分析:由于设备的状态切换历史非常频繁,所以表中的数据量非常庞大。假如用户的查询范围是2个月内全部设备,则查询原始数据,然后再进行各种复杂计算,可以想象,要想快速计算出结果,必然是一个相当大的挑战。

系统架构的限制:不幸的是,面板行业/半导体行业的报表系统架构,基本上是采用集中式架构。此需求根本无法应用分布式计算框架。只能通过精心设计程序和算法,充分压榨oracle服务器和应用服务器,实现大数据量的快速计算。

计算资源的分配:报表采用的WEB架构,所以不可能在本机计算。只能是在DB服务器或者应用服务器上进行计算。以此需求为例,从DB试select 1台设备1个月的原始数据,一共4万笔,大约需要10多秒钟。select 1天,一共1千多笔,则需要300ms。考虑到底层数据是设备状态的原始数据,而计算过程相当复杂,如果把所有的逻辑计算放在DB服务器,对用户而言,查询的响应时间必然很慢。所以要合理平衡DB服务器和应用服务器,分配合适的计算任务。

进行以上要素分析和试验之后,采用以下设计:

1,采用分治法。在oracle端,将原始数据按设备按天切割(切割点为每天的08:00),然后发给应用服务器。应用服务器采用java/C#等编程语言,对按天切割后的数据,进行复杂逻辑运算。然后应用服务器对子计算任务的结果进行合并,作为最终结果。

2,虽然有多个子计算任务,但由于DB服务器和应用器同时并发,所以整体的查询响应时间,优于仅在oracle 上运算的方案。

3,状态组合的逻辑、各单元的权重等信息,设计配置表,由用户来维护。维护表需要包含以下信息:

  • 设备单元信息。定义了各个设备、各级子单元等信息。
  • 设备的状态关键单元。定义每个设备要查看哪些关键单元。
  • 设备的状态组合与状态权重的对应关系。定义关键单元的每种状态组合下,其状态权重。

接下来进行按天查询指定设备算法的设计。

1,应用服务器同时请求查询原始数据表和配置信息表。

private DataTable QueryRawData(DateTime periodDate, string machineName)
{var sql = $@"select a.machinename as {RawDataTable.MACHINE_NAME}, a.timekey as {RawDataTable.EVENT_TIME},a.oldmachinestatename as {RawDataTable.PREV_STATUS}, a.machinestatename as {RawDataTable.CUR_STATUS}from CT_MACHINEHISTORY awhere 1=1and a.machinename like '{machineName}%'and timekey>='{periodDate.ToString("yyyyMMddHH")}'and timekey<'{periodDate.AddDays(1).ToString("yyyyMMddHH")}'order by a.machinename, a.timekey";var dtRaw = ConnectionKey.MESDBDG.Query(sql);return dtRaw;
}private DataTable QueryEqpSpec(string machineName)
{var sql = $@"select a.machinename as {SpecDataTable.MACHINE_NAME}, a.detailmachinetype as detail_machine_typefrom MACHINESPEC awhere 1=1and a.machinename like '{machineName}%'order by a.machinename";var dt = ConnectionKey.MESDBDG.Query(sql);return dt;
}private DataTable QueryEqpStatusFactorSpec(string machineName)
{var sql = $@"select a.MACHINE_NAME as {EqpStatusFactorSpecTable.MACHINE_NAME}, a.KEY_UNIT_LIST as {EqpStatusFactorSpecTable.KEY_UNIT_LIST},a.RATIO_LIST as {EqpStatusFactorSpecTable.RATIO_LIST},REGEXP_REPLACE(REGEXP_REPLACE(a.UNIT_STATUS_PATTERN,'\*','.'),',','') as {EqpStatusFactorSpecTable.UNIT_STATUS_PATTERN},a.DEFINED_MACHINE_STATUS as {EqpStatusFactorSpecTable.DEFINED_MACHINE_STATUS}from Eqp_Status_Factor_Spec awhere 1=1and a.MACHINE_NAME = '{machineName}'";var dt = ConnectionKey.RPTDB.Query(sql);return dt;
}

注:配置表有2个。分别在不同的DB。原因是其中一个DB为readonly,不能建表。而已有的配置信息不够全,需要完善。

2,找出该天中“缺少”的单元,并补出它在当天的状态。由于每个设备包含多个单元,而有些单元可能在该天无任何状态变化,则这样原始数据表中无此单元的记录。但是状态计算时,需要考虑到每个单元,所以需要“补偿”。基本思想就是,通过配置表,找到完整的单元集合。然后减去有实际抓取出来的单元集合,得到“缺少”的单元集合。然后将“缺少”的每个单元,找出其离指定最近的前一笔记录,该笔记录的设备单元状态,作为该单元在指定天起点和终点的状态(全天维持状态不变)。

要说明的是,如果存在多个“缺少”的单元,假如采用每笔问询一次oracle,则会大大降低整体查询效能。所以,这里优化设计,仅查询1次。

private DataTable CompensateMissedHistory(DateTime periodDate, DataTable dtEqpSpec, DataTable dtRaw)
{var dt1 = dtEqpSpec.SelectDistinct(SpecDataTable.MACHINE_NAME);var dt2 = dtRaw.SelectDistinct(RawDataTable.MACHINE_NAME);var dt3 = dt1.Except(dt2);if (dt3.Rows.Count > 0){var dt4 = QueryPrevRawData(periodDate, dt3.GetColumnData<string>(0));var dt5 = dt4.Clone();foreach (DataRow dr in dt4.Rows){dt5.Rows.Add(dr[0], periodDate.ToString("yyyyMMddHHmmssffffff"), dr[3], dr[3]);dt5.Rows.Add(dr[0], periodDate.AddDays(1).ToString("yyyyMMddHHmmssffffff"), dr[3], dr[3]);}return dt5;}return dt3;
}private DataTable QueryPrevRawData(DateTime periodDate, IEnumerable<string> machineNames)
{var sqlFormat = @"select * from(select a.machinename as {0}, a.timekey as {1},a.oldmachinestatename as prev_status, a.machinestatename as cur_statusfrom CT_MACHINEHISTORY awhere 1=1and a.machinename = '{2}'and timekey<'{3}'and rownum<=1 order by a.machinename, a.timekey desc)";var sql = new StringBuilder();var uninVerb = " union ";foreach (var machineName in machineNames){sql.AppendFormat(sqlFormat, RawDataTable.MACHINE_NAME, RawDataTable.EVENT_TIME, machineName, periodDate.ToString("yyyyMMddHH"));sql.Append(uninVerb);}sql = sql.Remove(sql.Length - uninVerb.Length, uninVerb.Length);var dt = ConnectionKey.MESDBDG.Query(sql);return dt;
}

3,将各单元按每天的时间切割点进行“对齐”。各单元在该天的首笔状态切换,不会刚好发生在切割点时间。所以需要补偿出从时间切割点到首笔状态变化之间的状态。同理,各单元在该天的末笔状态切换,也不会刚好发生在第二天的切割点时间。所以需要补偿出末笔到第二天切割点之间的状态。

private DataTable CompensateDateTimeBoundary(DateTime periodDate, DataTable dtRaw)
{var ds = dtRaw.GroupBy(RawDataTable.MACHINE_NAME);var startTime = periodDate.ToString("yyyyMMddHHmmssffffff");var endTime = periodDate.AddDays(1).ToString("yyyyMMddHHmmssffffff");for (var i = 1; i < ds.Tables.Count; i++){var dt = ds.Tables[i];if (!object.Equals(dt.Rows[0][1], startTime)){var dr1 = dt.NewRow();dr1[0] = dt.Rows[0][0];dr1[1] = startTime;dr1[3] = dr1[2] = dt.Rows[0][2];dt.Rows.InsertAt(dr1, 0);}if (!object.Equals(dt.Rows[dt.Rows.Count - 1][0], endTime)){var dr2 = dt.NewRow();dr2[0] = dt.Rows[dt.Rows.Count - 1][0];dr2[1] = endTime;dr2[3] = dr2[2] = dt.Rows[dt.Rows.Count - 1][3];dt.Rows.Add(dr2);}}var dtResult = ds.Tables[1].Clone();for (var i = 1; i < ds.Tables.Count; i++){dtResult = dtResult.UnionAll(ds.Tables[i]);}return dtResult;
}

4,至此,该天该设备的全部单元的切换历史,就准备完成了。不过进一步分析原始数据,发现有些单元会存在少量的这种情况:下一笔的状态和上一笔的状态相同。所以,为降低该天时间切割的区间数量,减少后续的计算量,可以对这种情况进行优化。也就说,对状态维持不变的多笔记录,"压缩“成1笔记录。

private DataTable CompressRawData(DataTable dtRaw)
{var dt = dtRaw.Clone();dt.ImportRow(dtRaw.Rows[0]);for (var i = 1; i < dtRaw.Rows.Count - 1; i++){if (dtRaw.Rows[i][3].ToString() == dtRaw.Rows[i - 1][3].ToString() &&//dtRaw.Rows[i][2].ToString() == dtRaw.Rows[i - 1][2].ToString() &&dtRaw.Rows[i][0].ToString() == dtRaw.Rows[i - 1][0].ToString())continue;dt.ImportRow(dtRaw.Rows[i]);}return dt;
}

5,得到这些数据后,就可以在应用服务器进行开始进行逻辑运算。为根据指定的状态组合组合和权重进行计算,有考虑设计关联查询的方案和类似掩码的方案。关联查询方案的方案理论可行,但是当设备的单元够多时(例如有N个单元),则各单元的可能的状态组合数是7的N次方。计算量非常庞大,实际不可行。采用类似掩码的方案,一番分析下来发现很难实现。首先是每个单元状态有7种,0和1的方案本身就无法代表。如果要扩展,则要判断状态时,会变得非常复杂,基本不可行。综合先来,唯一可行的解法,就是设计正则表达式。这样实现了以尽可能少的行覆盖了全部的状态组合。既方便用户建配置表,又能使程序快速得到权重规则,进而基于规则再进行计算。

考虑到为提高运算效率,分析7种状态,发现首字母各不相同,故分别用首字母代表各个状态。算法简述如下:

  • 首先,将设备单元的实际状态,提取首字母,“浓缩”成一个字符串。这个字符串代表当前设备各个单元的状态组合。
  • 接下来,以上述字符串作为正则表达式的输入,扫描配置表中用户定义的全部状态组合模式,进行正则表达式模式匹配。一旦匹配成功,取出该状态对应的权重计算规则。为方便用户建立和维护配置表,状态组合模式并非全部采用正则表达式语法,故需要在取配置表时做一定的替换。例如,用户建表时,让其输入‘*’代表任意字符,并将各状态以','分隔方便查看。取出来时要转成‘.’,且为了提高匹配效率,去掉了','。
  • 然后,分析权重计算规则,将设备实际的单元状态,回填到规则,生成各个状态的权重系数。权重规则中,是要捕获实际的状态值。这里有两种设计法。一种是按正则表达式的组捕获方法,来设计规则输入模式;另一种,采用字符串占位符,然后编写解析和回填方法。为提高程序运行效率且便于用户理解,这里采用了第二种设计。
  • 再下来,对已生成的各状态权重系数进行同类求和,得到最终结果。
public DataTable BuildResult(DateTime periodDate, string machineName)
{var dtRaw = QueryRawData(periodDate, machineName);var dtSpec = QueryEqpSpec(machineName);var dtFactorSpec = QueryEqpStatusFactorSpec(machineName);var dtMissed = CompensateMissedHistory(periodDate, dtSpec, dtRaw);var dtRawCompensated = CompensateDateTimeBoundary(periodDate, dtRaw);var dtRawComplete = dtRawCompensated.UnionAll(dtMissed);var dtRawCompressed = CompressRawData(dtRawComplete);var dtDateTimePoints = dtRawComplete.SelectDistinct(RawDataTable.EVENT_TIME).OrderByAsc(RawDataTable.EVENT_TIME);var dtResult = dtDateTimePoints.Copy();var ds = dtRawCompressed.GroupBy(RawDataTable.MACHINE_NAME);for (var i = 1; i < ds.Tables.Count; i++){var dt = ds.Tables[i].SelectFrom(RawDataTable.EVENT_TIME, RawDataTable.CUR_STATUS).RenameColumns(RawDataTable.CUR_STATUS, ds.Tables[0].Rows[i - 1][0].ToString());dtResult = dtResult.LeftJoin(dt, RawDataTable.EVENT_TIME);}for (var i = 1; i < dtResult.Rows.Count; i++){for (var j = 1; j < dtResult.Columns.Count; j++){if (dtResult.Rows[i][j] == DBNull.Value){dtResult.Rows[i][j] = dtResult.Rows[i - 1][j];dtResult.Rows[i][j] = dtResult.Rows[i - 1][j];}}}dtResult = Compute(dtResult, dtFactorSpec);return dtResult;
}

上述代码主要是完成原始数据的准备。包含为了方便计算,进行的空值补偿。

核心的计算逻辑如下:

public DataTable Compute(DataTable dtResult, DataTable dtFactorSpec)
{var dc = dtResult.AddColumn<string>("CombinedUnitStatus");var allStatus = new string[] { "E", "P", "T", "J", "D", "I", "R" };dtResult.AddColumns<decimal>(allStatus);var keyUnitList = dtFactorSpec.Rows[0][EqpStatusFactorSpecTable.KEY_UNIT_LIST].ToString().Split(',').Select(x => x.Trim()).ToArray();dc.Expression = keyUnitList.Select(x => $"SUBSTRING([{x}],1,1)").ToText('+');var patternList = dtFactorSpec.SelectFrom(EqpStatusFactorSpecTable.UNIT_STATUS_PATTERN).GetColumnData<string>(0).ToArray();var definedMachineStatusList = dtFactorSpec.SelectFrom(EqpStatusFactorSpecTable.DEFINED_MACHINE_STATUS).GetColumnData<string>(0).Select(x => x.Trim()).ToArray();for (var i = 0; i < dtResult.Rows.Count; i++){var dr = dtResult.Rows[i];var combinedStatus = dr["CombinedUnitStatus"].ToString();for (var j = 0; j < patternList.Length; j++){if (!Regex.IsMatch(combinedStatus, patternList[j]))continue;var definedStatus = definedMachineStatusList[j];var actualStatus = ReplaceWithActual(combinedStatus, definedStatus);var dictWeight = SumStatusWeight(actualStatus);foreach (var key in dictWeight.Keys){dr[key] = dictWeight[key];}break;}}return dtResult;
}

其中,根据实际值回填规则,生成各个状态的权重系数的方法如下:

private string ReplaceWithActual(string combinedStatus, string definedMachineStatus)
{if (!definedMachineStatus.Contains("{"))return definedMachineStatus;var patterns = definedMachineStatus.Split(',');var actualList = new string[patterns.Length];for (var i = 0; i < patterns.Length; i++){if (!patterns[i].StartsWith("{"))continue;var index = int.Parse(patterns[i].Substring(1, patterns[i].IndexOf('}') - 1));actualList[i] = $@"{combinedStatus[index]}:{patterns[i].Substring(patterns[i].IndexOf(':') + 1)}";}return actualList.ToText(',');
}

对同类权重系数求和的方法如下:

private Dictionary<string, decimal> SumStatusWeight(string acutalStatus)
{var dict = new Dictionary<string, decimal>();var actualStatusArray = acutalStatus.Split(',');foreach (var status in actualStatusArray){var kv = status.Split(':');if (!dict.ContainsKey(kv[0]))dict.Add(kv[0], Fraction.FromString(kv[1]).ToDecimal());elsedict[kv[0]] += Fraction.FromString(kv[1]).ToDecimal();}return dict;
}

至此,计算出某个设备在指定天的精细化的状态。后续的展示及更高一层的统计,便可以此为基础数据。

如果要计算多个设备在多天的精细化状态,便可采用多并行编程的方式,进行并行计算。这里不再赘述。

另外要说明的一点,为方便高效开发,程序设计中并未采用linq to dataset的方式进行数据查询和处理,而是自己开发出一套完整的DataTable Extension 流式API,对DataTable进行类似ORACLE式的操作,使得开发更高效,代码更简洁。例如代码中所示的LeftJoin, GroupBy等方法。

完整的程序截图如下。

一种精细化计算设备状态的方法相关推荐

  1. 两个形状不同的长方形周长_借助思维导图玩转小学阶段三种不同计算图形周长的方法!...

    小数老师说: 认识长方形.正方形.三角形等平面图形,周长是这些图形的一个基本特性.展开对周长的学习,首先要理解周长的意义,通常可以通过结合实例来理解,也可以经过实际的描边来具体感受周长的实际意义. 点 ...

  2. 边缘计算设备与部署方案

    1. 边缘计算设备 边缘计算设备,是相对于云计算而言的.不同于云计算的中心式服务,边缘服务是指在靠近物或数据源头的一侧,采用网络.计算.存储.应用核心能力为一体的开放平台,就近提供最近端服务.其应用程 ...

  3. 智能边缘计算设备介绍

    1 硬件网络图 智能边缘计算终端:1)可支持多种通信协议,可接入各种设备:2)在终端内基于IEC61131-3的优化控制软件计算后,把优化指令下发给控制器:3)同时通过通信协议把主站需要的信息上送给主 ...

  4. Android常用的蓝牙,GPS,网络等状态检测方法汇总

    序言 记录Android的一些判断网络,蓝牙,GPS,等设备状态的方法. 1.判断网络是否可用 // 是否有可用网络private boolean isNetworkConnected() {Conn ...

  5. 计算机图形图像学 ar,一种实现增强现实的方法及其应用、计算设备与流程

    本发明涉及计算机图形图像学技术领域,特别涉及一种实现增强现实的方法及其应用.计算设备. 背景技术: 在计算机辅助骨科手术中应用增强现实技术(augmentedreality,ar)具有非常高的临床应用 ...

  6. 电子计算机机房折旧提几年,IDC设备资产运营中四种“折旧率计算”的常见方法...

    原标题:IDC设备资产运营中四种"折旧率计算"的常见方法 数据中心基础设施设备管理中设备的折旧是固定资产的折旧.该基础设施设备或者IT设备在长期使用后仍可保持其原始物理形态,但由于 ...

  7. Android Framework 电源子系统(04)核心方法updatePowerStateLocked分析-2 循环处理  更新显示设备状态

    该系列文章总纲链接:专题分纲目录 Android Framework 电源子系统 本章关键点总结 & 说明: 本章节主要关注➕ updatePowerStateLocked 方法中 循环处理 ...

  8. 2种常见的设备稼动率OEE监测方法

    如何提高设备的利用率是制造业常见的问题,在技术上,一般采用监测设备稼动率OEE来计算,从而达到提高设备使用率.降低设备闲置率来提高经营效率.但是工厂车间设备环境复杂,存在布线困难.设备种类多的情况,因 ...

  9. 预测机器剩余使用寿命的可解释回归框架;基于磁场诱导Skyrmion动力学的神经形态计算模式识别;宽截面纳米带连续体内外的束缚态:一种新的递归S矩阵方法;脑-机接口:研究从视觉诱发电位到纯粹想象稳态电位

    可解释的机器学习 1)中文标题:预测机器剩余使用寿命的可解释回归框架 英文标题:An Explainable Regression Framework for Predicting Remaining ...

  10. 一种实现物联网设备自动注册及发现的方法与流程

    http://www.xjishu.com/zhuanli/62/201910087093.html 本发明涉及物联网应用领域,特别是涉及一种实现物联网设备自动注册及发现的方法. 背景技术: 早在上个 ...

最新文章

  1. Java实现xml和json互转
  2. RxSwift ViewModel定义
  3. vimrc 配置 史上最牛
  4. Python3之paramiko模块
  5. 化工仪表和自动化(自动控制系统)
  6. PS制作立体效果——圆柱
  7. angular input使用输入框filter格式化日期
  8. 开源纯C#工控网关+组态软件(八)表达式编译器
  9. 5000元性价比高的笔记本_2018性价比笔记本电脑品牌推荐 5000左右笔记本性价比推荐...
  10. python 白色怎么表示_python – 如何使用pil使用白色背景(透明?)的round_corner标识?...
  11. ~~试除法求所有约数(附模板题)
  12. 如何为MindManager时间表思维导图添加春节假期?
  13. CSDN帐号管理规范
  14. wifi信号强度测试软件 mac,Wifi Signal Strength for Mac(无线WiFi信号强度统计软件)
  15. android_基础_修改系统背景(状态栏颜色、导航栏颜色、标题栏颜色等等)
  16. Win10问题篇之——WIN2016和WIN10关闭同步主机服务,节省磁盘频繁读取,并关闭自动维护
  17. 非北京户口,户口地买房提取公积金
  18. JSP-java服务器端页面【学习笔记】
  19. 用科学计算机怎么计算指数,科学计算器e的幂次方怎么算 科学计算器怎么进行指数计算,我想算e的...
  20. python生成字典

热门文章

  1. linux数字对应的字母,Linux中的权限表示:字母表示和数字表示
  2. MT7921方案WIFI6无线网卡驱动编译方法
  3. (翻译)关系型数据库工作原理(二)
  4. 批量处理:读取文件夹,将json文件转化为txt文件
  5. 秋招公司真题刷题2019-2020java工程师
  6. 2020年显卡天梯图
  7. 自己的服务器进不去显示403,HTTP 403错误:含义和解决方法
  8. arduino超声波测距接线图详细_Arduino Uno + HY-SRF05 超声波测距模块详细讲解演示实验...
  9. 如何将本地图片转换成链接
  10. Python调用PyMol