什么是批次
药品是一批一批的生产,批次用于区分药品的不同生产批。一个生产批次包括以下信息:批次名、产品名、产品规格、所使用的配方、其他设定参数以及批次的开始时间和结束时间,另外还有生产过程中记录的数据。
以上数据并不全部存储在一起,而是在数据库中分成多个表存储。大致会分为配方、过程数据、批次、事件这样几个表。配方是某一种类型产品的生产参数,过程数据记录生产过程中实时的数据,批次表记录批次相关的信息以及未包括在配方中的设定参数,而批次的开始时间和结束时间记录在事件表中。事件表是以时间为序的一个序列,通过查询这个序列可以得到批次的开始时间和结束时间。
如何记录一个批次
基于以下设计的批次存储于用户归档。用户归档的本质是一个数据库。批次表的定义如下:
字段 | 类型 | 描述 |
BatchName | 字符串 | 批次名 |
BatchType | 数字(整型) | 批次类型 |
ProductName | 字符串 | 产品名 |
ProductType | 字符串 | 产品规格 |
RecipeID | 字符串 | 配方ID |
parameter1 | 参数1 | |
parameter2 | 参数2 | |
parameterN | 参数N |
向用户归档中添加数据需通过参数变量和控制变量。如果一个WinCC程序中管理了多台设备,则每台设备对应的变量名要加上设备前缀,这里用“XX_”代替。批次的用户归档中参数变量如下,除了XX_RecipeID和XX_BatchType外都是外部变量:
变量名 | 类型 | 描述 |
XX_BatchName | 文本变量8位字符集 | 批次名 |
XX_BatchType | 无符号的8位值 | 批次类型 |
XX_ProductName | 文本变量8位字符集 | 产品名称 |
XX_ProductType | 文本变量8位字符集 | 产品规格 |
XX_RecipeID | 文本变量8位字符集 | 配方ID |
XX_parameter1 | 非配方生产参数,这里用parameter指代,实际按需命名。 | |
XX_parameter2 | 非配方生产参数,这里用parameter指代,实际按需命名。 | |
XX_parameterN | 非配方生产参数,这里用parameter指代,实际按需命名。 |
批次的用户归档中控制变量如下:
变量名 | 类型 |
XX_UA_Batch_ID | 有符号的32位值 |
XX_UA_Batch_Job | 有符号的32位值 |
XX_UA_Batch_Field | 文本变量8位字符集 |
XX_UA_Batch_Value | 文本变量8位字符集 |
关于如何使用控制变量操作用户归档,以及封装的操作函数,详见《WinCC配方设计-基于用户归档》中的“如何用控制变量操作用户归档”,这里不再介绍。
批次表中带有一些参数的记录,这些参数可能是批次开始前的设置值,例如设定产量、生产速度,也可能是批次结束时记录的生产结果,例如实际产量、不合格数。实际操作过程中,可能先输入批次,再设定参数和配方,起初记录的批次可能只有批次名,其他字段的数据都是空值,所以在批次生产结束时,需要再更新一次批次表中的记录,以记录最终的生产结果。
批次表中不能出现两个重复的批次,记录批次前要用SQL查询是否已存在同名批次,不存在则新建记录,否则更新同名的批次。记录批次函数的代码如下:
Function RecordBatch(prefix, DSN) dim BatchName BatchName = HMIRuntime.Tags(prefix& "BatchName").Read if Trim(BatchName) = "" then '如果批次名为空,则不记录批次 RecordBatch = -1 Exit Function end if Dim cnn, rs, SQL Set cnn = CreateObject("ADODB.Connection") Set rs = CreateObject("ADODB.Recordset") '检查是否存在同名批次 SQL = "select BatchName from "& DSN &" where BatchName = '" & BatchName & "' " ConnectDatabase cnn,rs,SQL If rs.recordcount>0 Then RecordBatch = Update_UA(prefix& "UA_Batch", "BatchName", BatchName) else RecordBatch = Insert_UA(prefix& "UA_Batch") end if rs.Close cnn.Close Set rs = Nothing Set cnn = Nothing End Function
如何记录一个事件
事件数据库也基于用户归档存储。事件表定义如下:
字段 | 类型 | 描述 |
EventTime | 时间日期 | 时间戳 |
BatchName | 字符串 | 批次名 |
BatchType | 数字(整型) | 批次类型 |
EventMsg | 字符串 | 事件消息 |
StartLogFlag | 数字(整型) | 开始记录标志 |
UserName | 字符串 | 登录用户 |
HMIUserName | 字符串 | 设备登录用户 |
ProductionState | 数字(整型) | 生产状态 |
向事件数据库写入数据需要通过参数变量和控制变量进行。每台设备要有如下一组对应Event用户归档的变量,如果一个WinCC程序中管理了多台设备,则每台设备对应的变量名要加上设备前缀,这里用“XX_”代替。
有些变量建立在WinCC内部,在变量作用域中标为“内部”,有些变量来自PLC,在变量作用域中标为“外部”。
变量名 | 类型 | 描述 | 变量作用域 |
XX_EventTime | 日期/时间 | 触发事件时将用脚本写入当前的时间戳。 | 内部 |
XX_BatchName | 文本变量8位字符集 | 批次名,与批次表共用外部变量。 | 外部 |
XX_BatchType | 无符号的8位值 | 批次类型,与批次表共用变量。 | 外部或内部 |
XX_EventMsg | 文本变量8位字符集 | 事件消息 | 内部 |
XX_StartLogFlag | 二进制变量 | 开始记录标志 | 外部或内部 |
@CurrentUserName | 文本变量16位字符集 | 当前WinCC登录的用户名,系统中自带,不需建立,也不需要前缀。 | 内部 |
XX_HMICurrentUser | 文本变量16为字符集 | 设备上触摸屏上登录的用户。 | 外部 |
XX_ProductionState | 无符号的8位值 | 设备的生产状态。 | 外部 |
操作用户归档还需要一组控制变量控制变量如下:
变量名 | 类型 |
XX_UA_Event_ID | 有符号的32位值 |
XX_UA_Event_Job | 有符号的32位值 |
XX_UA_Event_Field | 文本变量8位字符集 |
XX_UA_Event_Value | 文本变量8位字符集 |
使用用户归档操作函数,定义记录事件函数,代码如下:
Function RecordEvent(prefix, eventMsg) dim SWDT, utcTime Set SWDT = CreateObject("WbemScripting.SWbemDateTime") SWDT.SetVarDate Now, True utcTime = SWDT.GetVarDate (False) HMIRuntime.Tags(prefix& "EventTime").Write utcTime HMIRuntime.Tags(prefix& "EventMsg").Write eventMsg RecordEvent = Insert_UA(prefix& "UA_Event") End Function
参数:
- prefix:设备的前缀,例如“XX_”。
- eventMsg:需要记录的事件。
返回值:
- 0:记录成功。
- -1:记录失败。
何时触发事件和批次的记录
在开始生产时,设备中会有某个指示启停的变量被置1,结束生产时则被置0。如果有多种批次类型,每个批次类型有不同的启停变量。在VBS全局动作中,把这些变量做为触发器,当变量改变就会触发执行一段代码,这段代码调用StartEndCycle()函数,该函数内再调用RecordEvent()和RecordBatch()函数。
假设有3个批次类型,对应的变量名如下:
变量名 | 描述 |
XX_StartFlag1 | 批次类型1启动变量 |
XX_StartFlag2 | 批次类型2启动变量 |
XX_StartFlag3 | 批次类型3启动变量 |
将不同的批次类型对应的类型标志、变量名、事件消息添加到数组,对数组遍历,使用相同的代码依次检查每个启停变量,然后触发事件和批次记录。全局VBS动作中的代码如下:
'File Name:XX_StartEndCycle Function action If HMIRuntime.Tags("@ServerName").Read <> HMIRuntime.Tags("@LocalMachineName").Read Then Exit Function End If 'BatchType, TagName, StartMsg, EndMsg Dim batchItems batchItems = Array(_ Array(1,"StartFlag1",TranslateText("开始类型1批次记录", 2052),TranslateText("结束类型1批次记录", 2052)),_ Array(2,"StartFlag2",TranslateText("开始类型2批次记录", 2052),TranslateText("结束类型2批次记录", 2052)),_ Array(3,"StartFlag3",TranslateText("开始类型3批次记录", 2052),TranslateText("结束类型3批次记录", 2052))_ ) Call StartEndCycle("XX_", batchItems) End Function
StartEndCycle()代码如下:
Sub StartEndCycle( prefix, batchItems ) If Trim(HMIRuntime.Tags( prefix & "BatchName" ).read) = "" Then '如果批次名为空,则退出函数 HMIRuntime.Tags( prefix & "BatchType" ).Write 0 Exit Sub end if Dim batchItem For Each batchItem In batchItems If Not TagQcIsBad(prefix & batchItem(1)) Then If HMIRuntime.Tags( prefix & batchItem(1) ).Read = 1 Then '检查触发变量是否为1 HMIRuntime.Tags( prefix & "BatchType" ).Write batchItem(0) '设置批次类型 HMIRuntime.Tags( prefix & "StartLogFlag").Write 1 Call RecordEvent( prefix, batchItem(2) ) '记录事件 Call RecordBatch( prefix, "UA#"&prefix&"Batch" ) else If HMIRuntime.Tags( prefix & "BatchType" ).Read = batchItem(0) Then '检查是否是该批次类型 HMIRuntime.Tags( prefix & "StartLogFlag" ).Write 0 Call RecordEvent( prefix, batchItem(3) ) '记录事件 Call RecordBatch( prefix, "UA#"&prefix&"Batch" ) HMIRuntime.Tags( prefix & "BatchType" ).Write 0 End If End If End If Next End Sub
批次操作流程
- 开始生产一批药之前,首先给制药设备输入这批药的批次号、产品名称、产品规格,以及输入生产的参数和配方。输入批次号时需通过程序查询批次表中是否存储同名的批次,不能有两个重名的批次号。以上内容的输入不分先后顺序,在开始生产之前都可以修改。这时输入的内容并未记入数据库。
- 设备开始生产之前可能需要试机,这段时间需保持批次启停指示变量为0,设备应运行在调试模式;或者设计为手动启停批次,使得机器的运转启停不影响批次的启停。完成试机之后,将设备转为自动运行模式,机器启动以后批次指示变量置为1;需手动启动批次的,则在设备的操作屏上点击“启动批次”按钮,将批次启停指示变量置1。
- 批次启停指示变量改变后,WinCC的全局动作中的代码将被触发,从而记录批次和事件。此时数据库中还没有这个批次的记录,代码将在批次数据库中新建一条记录,把批次号、产品名称、配方等内容记录到数据库。同时向事件数据库中新增一条数据,记录了批次开始的时间戳。
- 在正常生产时,不应再编辑批次号、产品名称等内容,须要通过代码限制界面中的内容输入。
- 完成生产之后,随着生产结束自动将批次启停指示变量置为0,或者在设备操作屏上点击“结束批次”手动将变量设为0。
- 批次启停指示变量的变化将再次触发全局VBS动作。批次函数中的代码将查询到批次数据库中已存在这个批次号的记录,然后更新数据库中这个批次的内容,最终把批次结果的参数写入到数据库中。而事件数据库中将再新增一条数据,记录批次结束的时间戳。
- 为了避免生产完之后设备调试时意外覆盖之前的批次记录,应在生产完之后手动清空批次变量中的值,空的批次不会被记录到数据库,输入重名的批次名也不被程序允许。
批次输入界面
批次输入界面如下:
控件输出值绑定的变量
产品名称、产品规格、产品批号和当前配方都是“输入/输出域”控件,在控件的“输出值”属性处分别绑定了变量:ProductName, ProductType, BatchName, RecipeID。示例如下图:
生产状态是文本列表控件,“输出值”属性绑定了变量ProdutionState,如下:
以上变量都没有添加变量前缀“XX_”,因为会通过画面窗口控件调用这个画面,在画面窗口的属性中添加前缀,画面的所有变量将自动带上前缀。
启动批次后禁止操作控件
灰色的文本控件代表只读,而白色的控件代表可读写。产品名称、产品规格和产品批号三个控件在未启动批次时可编辑,批次启动以后不能被编辑,并且控件颜色变为灰色。批次清空按钮也只能在批次未运行时可按下,批次运行时按钮将被禁用。
在控件的“允许操作员控制”属性中,设置动态对话框,当前StartLogFlag为0时将改属性设为“是”,为1时设为“否”。
在产品名称、产品规格和产品批号三个控件的“背景颜色”属性中设置动态对话框,当StartLogFlag为0时为白色,为1时为灰色。
输入批次号
点击产品批号“输入/输出域”控件后,弹出对话框要求输入产品批号,然后对输入的批号检查是否重复。
Sub OnClick(ByVal Item) Dim BatchName BatchName = Trim(Inputbox(TranslateText("请输入产品批号:",2052),TranslateText("输入框",2052),Item.OutputValue)) If BatchName<>"" Then If FindDuplicateBatchName("UA#XX_Batch",BatchName)=0 Then HMIRuntime.tags("BatchName").Write BatchName End If End If End Sub
检查重复批次的代码如下:
Function FindDuplicateBatchName(UAName,BatchName) FindDuplicateBatchName = 0 Dim cnn, rs, SQL Set cnn = CreateObject("ADODB.Connection") Set rs = CreateObject("ADODB.Recordset") SQL = "select BatchName from "&UAName&" where BatchName='" & BatchName & "'" ConnectDatabase cnn,rs,SQL If rs.recordcount>0 Then Msgbox TranslateText("产品批号重复!", 2052) FindDuplicateBatchName = 1 End If rs.Close cnn.Close End Function
如何查询批次
未完待续...