WinCC配方設計-基於用戶歸檔


配方是給機器設定的一組運行參數,當機器要生產不同規格的產品時,可以給機器設定不同的參數。

配方的設計要點之一是如何保存配方。制葯行業要求對生產數據一般保存5年,生產數據包括生產時所用的配方,當打印報表時需要把對應的配方打印出來。這就要求已生產過的配方要能長期保存,不能被修改或刪除。

以下內容介紹如何基於WinCC的用戶歸檔存儲配方。用戶歸檔是對SQL數據庫的一種封裝,與直接讀寫數據庫相比,用戶歸檔可以滿足雙機熱備的要求。

用戶歸檔的讀寫要求

用戶歸檔實質是存儲在SQL Server中的數據庫,優點是基於WinCC軟件實現了兩台電腦里數據庫的雙機熱備。SQL Server也有一些熱備方法,例如“數據庫復制:發布-訂閱”,但是因為WinCC的限制不能使用,用戶歸檔的熱備在此更合適。

要實現用戶歸檔的熱備冗余,不能直接寫數據庫,腳本中只能通過UA API函數、控制變量對用戶歸檔寫入,對用戶歸檔讀取可以直接讀數據庫。UA API函數只在C腳本中支持,用C腳本讀寫配方過於復雜,所以在VB腳本中使用控制變量操作配方。

 

配方的數據流向

用控制變量操作配方,只能將配方從變量寫入到用戶歸檔的數據庫,或者將特定配方從用戶歸檔的數據庫寫入到變量。實際使用中,需要先查看配方,然后再將配方下載到PLC,因此需要兩套不同的變量,其中一組變量作為中間變量(內部變量)用於查看、編輯,另一組變量是PLC中的實際變量(外部變量),將中間變量的值寫入到這組變量就意味着下發了配方。數據流圖如下。

 

 用戶歸檔數據庫中的特殊字段

 

紅框中的字段是每個配方數據庫表中都統一的字段,以下是這些字段的說明。

字段名稱 類型 描述
Recipe_ID 字符串 通過Recipe_ID唯一標識一個配方,該字段由Recipe_No和Recipe_Edition拼接而成。
Recipe_No 字符串 新建配方時指定唯一配方編號,修改配方將生產新的配方版本,不會覆蓋舊配方,新舊配方的配方編號相同。 
Recipe_Edition 字符串  配方的版本,新建的配方的版本為1,修改並保存后配方版本自動加1,再與配方編號拼接成新的配方ID,在數據庫中存儲一條新的數據。
Recipe_Name 字符串  配方名稱。
Recipe_Description  字符串 配方描述,同一配方的不同版本的描述可以不同。 
IsProduced  數字(整型)  已生產標志位,當配方已下發並執行了生產,將這個配方的標志位置1,否則這個標志位為0。
IsDeleted  數字(整型)  當刪除一個配方時,如果IsProduced為1,則把IsDeleted標志位置1,表示已刪除,查看配方時不再顯示這條配方,但配方依然存儲在數據庫中,打印報表時依然可以調出這個配方的數據;如果IsProduced為0,說明這個配方沒有生產過,將會從數據庫中刪除。

使用控制變量操作用戶歸檔,則必須給每個字段值綁定參數變量,然后將參數變量的值寫入到用戶歸檔。

綁定的參數變量如下,“XX_”代表設備前綴,不同的設備指定不同的設備前綴。設備前綴之后的變量名在所有設備中都是統一的,之后的代碼根據這些變量名傳遞配方信息。“M_”代表這是中間變量(內部變量),還有一組不帶“M_”中綴的同名變量,那些是外部變量,下載配方時通過中間變量向外部變量寫值完成。

變量名 類型
XX_M_RecipeID 文本變量8位字符集
XX_M_RecipeNo 文本變量8位字符集
XX_M_RecipeEdition 文本變量8位字符集
XX_M_RecipeName 文本變量8位字符集
XX_M_RecipeDescription 文本變量8位字符集
XX_M_IsProduced 二進制變量
XX_M_IsDeleted 二進制變量

 

如何用控制變量操作用戶歸檔

用戶歸檔需要綁定四個變量對其進行操作,這四個變量分別為ID、Job、Field、Value,控制變量的說明如下: 

控制變量

功能

數據類型

ID

用戶歸檔的數據記錄編號

有符號 32 位數

Job

可能存在下列作業:

“6”= 讀取變量寫入到用戶歸檔中的數據記錄

“7”= 將數據記錄從用戶歸檔寫入變量

“8”= 刪除用戶歸檔中的數據記錄

執行作業后,“作業”變量將變為以下數值:

“0”= 無錯誤

“-1”= 有錯誤

有符號 32 位數

Field

用戶歸檔的特定字段

文本變量,8 位

Value

特定用戶歸檔字段的值

文本變量,8 位

控制變量“ID”和“作業”的組合:

ID

作業 =“6”

作業 =“7”

作業 =“8”

-1

讀取變量向用戶歸檔中新增數據記錄

-

刪除最低 ID 的數據記錄

-6

讀取變量寫入到最低 ID 的數據記錄

讀取最低 ID 的數據記錄寫入到變量

刪除最低 ID 的數據記錄

-9

讀取變量寫入到最高 ID 的數據記錄

讀取最高 ID 的數據記錄寫入到變量

刪除最高 ID 的數據記錄

>0

讀取變量寫入到ID變量指定的數據記錄

讀取ID變量指定的數據記錄寫入到變量

刪除ID變量指定的數據記錄

0

讀取變量寫入到字段變量和值變量指定的數據記錄 讀取字段變量和值變量指定的數據記錄寫入到變量 刪除字段變量和值變量指定的數據記錄

 

操作用戶歸檔的全局函數

用控制變量操作用戶歸檔的方式不太明晰,寫成全局變量可以使該過程更加簡便,常用的方式是用Field變量和Value變量確定一條記錄,以下是寫在全局腳本中操作用戶歸檔的函數。用控制變量操作用戶歸檔是一個異步的過程,函數中添加了檢查Job變量返回值的代碼,變成了同步過程,函數返回0表示執行成功,返回-1表示執行失敗。

注意:雖然是全局函數,但是在設定了前綴的畫面窗口中調用時,全局函數中使用的變量也會被加上前綴。

Function Delete_UA(strUAName,strField,strValue)'刪除一條信息
	HMIRuntime.Tags(strUAName&"_ID").Write 0
	HMIRuntime.Tags(strUAName&"_Field").Write strField
	HMIRuntime.Tags(strUAName&"_Value").Write strValue
	HMIRuntime.Tags(strUAName&"_Job").Write 8
	Do
		Delete_UA = HMIRuntime.Tags(strUAName&"_Job").Read
	Loop Until Delete_UA=0 Or Delete_UA=-1
End Function

Function Select_UA(strUAName,strField,strValue) '下載一條信息	
	HMIRuntime.Tags(strUAName&"_ID").Write 0
	HMIRuntime.Tags(strUAName&"_Field").Write strField
	HMIRuntime.Tags(strUAName&"_Value").Write strValue
	HMIRuntime.Tags(strUAName&"_Job").Write 7
	Do
		Select_UA = HMIRuntime.Tags(strUAName&"_Job").Read
	Loop Until Select_UA=0 Or Select_UA=-1
End Function

Function Insert_UA(strUAName)'添加一條信息
	HMIRuntime.Tags(strUAName&"_ID").Write -1
	HMIRuntime.Tags(strUAName&"_Field").Write ""
	HMIRuntime.Tags(strUAName&"_Value").Write ""
	HMIRuntime.Tags(strUAName&"_Job").Write 6
	Do
		Insert_UA = HMIRuntime.Tags(strUAName&"_Job").Read
	Loop Until Insert_UA=0 Or Insert_UA=-1
End Function

Function Update_UA(strUAName,strField,strValue)'修改一條信息
	HMIRuntime.Tags(strUAName&"_ID").Write 0
	HMIRuntime.Tags(strUAName&"_Field").Write strField
	HMIRuntime.Tags(strUAName&"_Value").Write strValue
	HMIRuntime.Tags(strUAName&"_Job").Write 6
	Do
		Update_UA = HMIRuntime.Tags(strUAName&"_Job").Read
	Loop Until Update_UA=0 Or Update_UA=-1
End Function

  

配方界面概覽

 

操作配方的全局函數

上圖的新建、保存、下載、上載和刪除按鈕中會用到許多腳本,這些腳本並不直接寫在控件中,而是以函數形式定義在VBS全局項目模塊中,然后在控件中調用函數。一個WinCC程序可能要管理許多台設備的配方,也就有許多張配方畫面,假如直接將腳本寫在控件中,當需要修改腳本時,會要修改很多遍。所以將腳本集中定義在全局項目模塊,便於維護代碼。

這些函數中通過控件名稱調用控件,必須在配方畫面中才能使用,並且控件的命名必須依照給定的名稱。

 調用函數會用到三個參數:

  • tagPrefix:變量前綴。不同設備的變量都帶有不同的變量前綴,避免變量名沖突。
  • DSN:數據源名稱。配方數據表的名稱,建議用戶歸檔以“變量前綴+Recipe”命名,實際在數據庫中還會加上“UA#”前綴。例如,假設變量前綴是“XX_”,則DSN為“UA#XX_Recipe”。
  • objNameList:這是一個二維數組,第一列是配方數據表字段名和畫面控件的名稱,要求配方數據表字段名和被編輯的控件的名稱一致;第二列是變量名。

 全局項目模塊中的代碼如下(建議文件命名為_Recipe.bmo):

'該文件中的函數僅用於當前項目的recipe,目的是為了方便編輯,不具有移植性。

'新建配方
Sub NewRecipe(tagPrefix, DSN, objNameList)
    '-----------------------------------------------------
    ' 新建配方
    ' 輸入配方編號、配方名和配方描述,
    ' 檢查配方編號是否與已有的配方重復,
    ' 將配方信息寫入到界面控件中顯示。
    '-----------------------------------------------------
    '詢問是否新建配方
    Dim RecipeNo,RecipeName,RecipeDescription
    If ScreenItems("BT_Save").Enabled=True Then  '檢查是否有配方正在編輯
        If Msgbox (TranslateText("配方正在編輯,確定不保存該配方,繼續新建配方嗎?", 2052),vbOKCancel+vbQuestion,"Note") = vbCancel Then
            Exit Sub
        End If
    Else
        If Msgbox (TranslateText("新建配方嗎?",2052),vbOKCancel+vbQuestion,"Note") = vbCancel Then
            Exit Sub
        End If
    End If

    '創建數據庫對象
    Dim cnn, rs
    Set cnn = CreateObject("ADODB.Connection")
    Set rs = CreateObject("ADODB.Recordset")

    '輸入配方編號
    Do While True
        RecipeNo = Trim(InputBox(TranslateText("請輸入新配方編號:", 2052),"Note"))
        '檢查輸入的配方編號是否符合要求
        If RecipeNo="" Or Check_LawlessChar(RecipeNo)=True Then
            If MsgBox (TranslateText("配方編號只能包含字母和數字,請重新輸入!", 2052),vbRetryCancel + vbInformation,"Note") = vbCancel Then
                Exit Sub
            Else
                '如果選擇重試則繼續執行循環
            End If
        else
            '檢查配方編號是否重復
            Dim SQL
            SQL = "select Recipe_No from "& DSN &" where Recipe_No='" & RecipeNo & "' AND IsDeleted = 0"
            ConnectDatabase cnn,rs,SQL
            If rs.recordcount>0 Then
                If MsgBox (TranslateText("配方編號重復!",2052),vbRetryCancel + vbInformation,"Note") = vbCancel Then
                    rs.Close
                    cnn.Close
                    Exit Sub
                else
                    '如果選擇重試則繼續執行循環
                End If
            Else
                rs.Close
                cnn.Close
                Exit Do
            End If
        End If
    Loop
    Set rs = Nothing
    Set cnn = Nothing
    
    '輸入配方名稱
    Do While True
        RecipeName = Trim(InputBox(TranslateText("請輸入新配方名稱:",2052),"Note"))
        If RecipeName="" Then
            If MsgBox (TranslateText("配方名稱不能為空!",2052),vbRetryCancel + vbInformation,"Note") = vbCancel Then
                Exit Sub
            else 
                '如果選擇重試則繼續執行循環
            End If
        Else    
            Exit Do
        End If
    Loop

    '輸入配方描述
    RecipeDescription = Trim(InputBox(TranslateText("請輸入新配方描述(可以為空):",2052),"Note"))
    
    '清空控件內容
    Dim objName
    For Each objName In objNameList
        ScreenItems(objName(0)).text = ""
    Next

    '向控件填入配方信息
    ScreenItems("ComboRecipeList").text    = RecipeNo
    ScreenItems("ComboEditionList").text   = "0"
    ScreenItems("Recipe_Name").text        = RecipeName
    ScreenItems("Recipe_Description").text = RecipeDescription

    '設置按鈕狀態
    ScreenItems("BT_New").Enabled          = False
    ScreenItems("BT_Download").Enabled     = False
    ScreenItems("BT_Save").Enabled         = True
    ScreenItems("BT_Delete").Enabled       = False
    ScreenItems("ComboRecipeList").Enabled = True
End Sub

'*******************************************************************************************************************************************

'讀取數據庫中的配方編號寫入到ComboBox控件
Sub WriteRecipeNoToComboBox(DSN, objName)
    '創建數據庫對象
    Dim cnn, rs
    Set cnn = CreateObject("ADODB.Connection")
    Set rs = CreateObject("ADODB.Recordset")
    '查詢不為空並且未刪除的配方編號
    Dim SQL
    SQL = "Select distinct Recipe_No FROM "& DSN &" where Recipe_No<>'' AND Recipe_No IS NOT NULL AND IsDeleted = 0"
    ConnectDatabase cnn,rs,SQL
    '將配方編號寫入到控件
    ScreenItems(objName).clear
    If rs.RecordCount>0 Then
        rs.MoveFirst        
        Dim i
        For i = 1 To rs.RecordCount
            ScreenItems(objName).AddItem rs("Recipe_No")
            rs.MoveNext
        Next
    End If
    rs.Close
    Set rs = Nothing
    cnn.Close
    Set cnn = Nothing
End Sub

'*******************************************************************************************************************************************

'保存配方
Sub SaveRecipe(tagPrefix, DSN, objNameList)
    '--------------------------
    ' 保存配方:
    ' 查找配方編號的最大版本號,
    ' 將最大版本號遞增置為下一版本號,
    ' 將配方信息寫入到中間變量,
    ' 將界面上的配方參數寫入到中間變量,
    ' 將中間變量的值寫入到用戶歸檔中新增一條記錄,
    ' 保存配方並不會修改已存在的配方,始終是增加一個新版本的配方。
    '--------------------------
    
    '檢查是否選擇配方                                                                                                        
    If ScreenItems("ComboRecipeList").text="" Or ScreenItems("ComboEditionList").text="" Then
        MsgBOX TranslateText("沒有選擇正確的配方,不能保存",2052)
        Exit Sub
    End If                       
                
    '電子簽名
    Dim sComments
    If EsigDialog(sComments,False,"") <> 1 Then
        Exit Sub 
    End If

    '創建數據庫對象
    Dim cnn, rs
    Set cnn = CreateObject("ADODB.Connection")
    Set rs = CreateObject("ADODB.Recordset")
            
    '查找數據庫中配方的最大版本號
    Dim intMaxID,i
    intMaxID=0 
    Dim SQL
    SQL = "select Recipe_Edition from "& DSN &" where Recipe_No='" & RecipeNo & "'"
    ConnectDatabase cnn,rs,SQL
    If rs.recordcount>0 Then
        rs.MoveFirst
        For i = 1 To rs.recordcount
            If intMaxID<CInt(Mid(rs("Recipe_Edition"),1)) Then ' 查找最大配方版本號
                intMaxID=CInt(Mid(rs("Recipe_Edition"),1))
            End If
            rs.MoveNext
        Next 
    End If
    '關閉數據庫連接
    rs.Close
    cnn.Close
    Set rs = Nothing
    Set cnn = Nothing

    '計算配方下一版本號
    NextRecipeEdition = intMaxID + 1

    '清空中間變量的已刪除和已生產標志
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_IsProduced").Write 0
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_IsDeleted").Write 0
    
    '獲取配方ID
    Dim recipeID
    recipeID = ScreenItems("ComboRecipeList").text &"_"& NextRecipeEdition
    
    '將界面顯示的配方信息寫入內部變量
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeNo").Write           ScreenItems("ComboRecipeList").text
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeEdition").Write      NextRecipeEdition
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeName").Write         ScreenItems("Recipe_Name").text
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeDescription").Write  ScreenItems("Recipe_Description").text
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeID").Write           recipeID
  

    '將界面顯示的配方參數寫入內部變量
    Dim objName
    For Each objName In objNameList
        If Trim(ScreenItems(objName(0)).text) = "" Then 
            HMIRuntime.Tags("@NOP::" &tagPrefix& "M_"& objName(1)).Write 0
        Else
            HMIRuntime.Tags("@NOP::" &tagPrefix& "M_"& objName(1)).Write ScreenItems(objName(0)).text
        End If
    Next
    
    '保存配方,把變量值寫入到數據庫
    If Insert_UA("@NOP::" &tagPrefix& "UA_Recipe") <> 0 Then
        Msgbox TranslateText("保存失敗!",2052)
        Exit Sub        
    End If
   
   '記錄Audit
    CreateOpMsg PrefixToName(tagPrefix), TranslateText("保存配方",2052)&recipeID , "", recipeID, sComments 
        
    '更新配方編號控件中的內容               
    Call WriteRecipeNoToComboBox(DSN, "ComboRecipeList")
    
    '向界面控件添加配方版本
    ScreenItems("ComboEditionList").AddItem NextRecipeEdition
    ScreenItems("ComboEditionList").text =  NextRecipeEdition
   
    Msgbox TranslateText("保存升級成功",2052)
    
    '設置按鈕狀態
    ScreenItems("BT_New").Enabled=True
    ScreenItems("BT_Download").Enabled=True
    ScreenItems("BT_Delete").Enabled=True
    ScreenItems("BT_Save").Enabled=False  
    ScreenItems("ComboRecipeList").Enabled=True
End Sub

'*******************************************************************************************************************************************

'下載配方
Sub DownloadRecipe(tagPrefix, DSN, objNameList)
    '---------------------------------------
    ' 下載配方:
    ' 從用戶歸檔中讀取配方到中間變量,
    ' 再把配方從中間變量寫入到外部變量。
    '---------------------------------------
              
'   If HMIRuntime.Tags("A_CommunicationFail").Read=1 Then 
'       MsgBOX "通訊失敗,不能下載"
'       Exit Sub
'    End If 

    '檢查是否選擇配方
    If ScreenItems("ComboRecipeList").text="" Or ScreenItems("ComboEditionList").text="" Then
        MsgBOX TranslateText("沒有選擇正確的配方,不能下載!",2052)
        Exit Sub
    End If  
    
    '電子簽名
    Dim sComments
    If EsigDialog(sComments,False,"") <> 1 Then
        Exit Sub 
    End If
    
    '計算配方ID
    Dim RecipeID 
    RecipeID = ScreenItems("ComboRecipeList").text&"_"&ScreenItems("ComboEditionList").text                 
    
    '更新中間變量
    If Select_UA( "@NOP::" &tagPrefix& "UA_Recipe","Recipe_ID",RecipeID) <> 0 Then
        MsgBOX TranslateText("獲取配方失敗!",2052)
        Exit Sub
    end if
    
    '將配方從中間變量寫入到外部變量
    Dim objName
    For Each objName In objNameList
        HMIRuntime.Tags("@NOP::" &tagPrefix& objName(1)).Write HMIRuntime.Tags("@NOP::" &tagPrefix& "M_"&objName(1)).Read
    Next
    HMIRuntime.Tags("@NOP::" &tagPrefix& "RecipeNo").Write          HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeNo").Read
    HMIRuntime.Tags("@NOP::" &tagPrefix& "RecipeEdition").Write     HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeEdition").Read
    HMIRuntime.Tags("@NOP::" &tagPrefix& "RecipeName").Write        HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeName").Read
    HMIRuntime.Tags("@NOP::" &tagPrefix& "RecipeDescription").Write HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeDescription").Read
    HMIRuntime.Tags("@NOP::" &tagPrefix& "RecipeID").Write          HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeID").Read
    
    '記錄Audit
    CreateOpMsg PrefixToName(tagPrefix), TranslateText("下載配方",2052)&recipeID , "", recipeID, sComments 
        
    '設置按鈕狀態
    ScreenItems("BT_New").Enabled=True
    ScreenItems("BT_Download").Enabled=True
    ScreenItems("BT_Delete").Enabled=True
    ScreenItems("ComboRecipeList").Enabled=True
    
    Msgbox TranslateText("下載成功",2052)
End Sub

'*******************************************************************************************************************************************

'上載配方
Sub Upload(tagPrefix, DSN, objNameList)
    '檢查是否有配方正在編輯
    If ScreenItems("BT_Save").Enabled=True Then
        If Msgbox (TranslateText("配方正在編輯,確定不保存該配方,繼續上載配方嗎?",2052),vbQuestion+vbOKCancel,"Note") = vbCancel Then
            Exit Sub
        End If
    End If
    
    '檢查是否選擇了配方編號,詢問是否上傳到當前配方編號並新建配方版本。
    Dim NewRecipeNoFlag
    If Trim(ScreenItems("ComboRecipeList").text) <> "" And _
       Trim(ScreenItems("Recipe_Name").text) <> "" Then  ' 判斷是否沿用當前選擇的配方編號
        Select Case MsgBOX( TranslateText("是否上傳到當前配方編號並更新配方版本?",2052), vbYesNoCancel + vbQuestion)
            Case vbYes
                NewRecipeNoFlag = False
            Case vbno
                NewRecipeNoFlag = True
            Case Else
                Exit Sub
        End Select
    Else
        NewRecipeNoFlag = True
    End If    

    '電子簽名
    Dim sComments
    If EsigDialog(sComments,False,"") <> 1 Then
        Exit Sub 
    End If
    
    '創建數據庫對象
    Dim cnn, rs, SQL
    Set cnn = CreateObject("ADODB.Connection")
    Set rs = CreateObject("ADODB.Recordset")
    
    Dim RecipeNo, RecipeName, RecipeDescription
    If NewRecipeNoFlag = True Then  ' 輸入新配方編號和配方名稱
        Do While True
            RecipeNo = Trim(InputBox(TranslateText("請輸入新配方編號:",2052),"Note"))
            '檢查輸入的配方編號是否符合要求
            If RecipeNo="" Or Check_LawlessChar(RecipeNo)=True Then
                If MsgBox (TranslateText("配方編號只能包含字母和數字,請重新輸入!",2052),vbRetryCancel + vbInformation,"Note") = vbCancel Then
                    Exit Sub
                Else
                    '如果選擇重試則繼續執行循環
                End If
            else
                '檢查配方編號是否重復
                SQL = "select Recipe_No from "& DSN &" where Recipe_No='" & RecipeNo & "' AND IsDeleted = 0"
                ConnectDatabase cnn,rs,SQL
                If rs.recordcount>0 Then
                    If MsgBox (TranslateText("配方編號重復!",2052),vbRetryCancel + vbInformation,"Note") = vbCancel Then
                        rs.Close
                        cnn.Close
                        Exit Sub
                    else
                        '如果選擇重試則繼續執行循環
                    End If
                Else
                    rs.Close
                    cnn.Close
                    Exit Do
                End If
            End If
        Loop

        '輸入配方名稱
        Do While True
            RecipeName= Trim(InputBox(TranslateText("請輸入新配方名稱:",2052),"Note"))
            If RecipeName="" Then
                If MsgBox (TranslateText("配方名稱不能為空!",2052),vbRetryCancel + vbInformation,"Note") = vbCancel Then
                    Exit Sub
                else 
                    '如果選擇重試則繼續執行循環
                End If
            Else    
                Exit Do
            End If
        Loop

    Else '使用當前配方編號和配方名稱
        RecipeNo = Trim(ScreenItems("ComboRecipeList").text)
        RecipeName = Trim(ScreenItems("Recipe_Name").text)
    End If

    '輸入配方描述
    RecipeDescription = Trim(InputBox(TranslateText("請輸入新配方描述(可以為空):",2052),"Note"))

    '查找特定配方編號下最大版本號
    Dim intMaxID,i
    intMaxID = 0
    SQL = "select Recipe_Edition from "& DSN &" where Recipe_No='" & RecipeNo & "'"
    ConnectDatabase cnn,rs,SQL
    If rs.recordcount>0 Then
        rs.MoveFirst
        For i = 1 To rs.recordcount
            If intMaxID<CInt(Mid(rs("Recipe_Edition"),1)) Then ' 查找最大配方版本號
                intMaxID=CInt(Mid(rs("Recipe_Edition"),1))
            End If
            rs.MoveNext
        Next
    End If
    
    '設置下一版本號
    NextRecipeEdition = intMaxID + 1

    '計算配方ID
    Dim recipeID
    recipeID = RecipeNo &"_"& NextRecipeEdition
    '將配方參數從外部變量寫入到中間變量
    Dim objName
    For Each objName In objNameList
        HMIRuntime.Tags("@NOP::" &tagPrefix& "M_"&objName(1)).Write HMIRuntime.Tags("@NOP::" &tagPrefix& objName(1)).Read
    Next

    '清空中間變量已刪除和已生產標志
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_IsProduced").Write 0
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_IsDeleted").Write 0
        
    '將輸入的配方信息寫入內部變量
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeNo").Write           RecipeNo
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeEdition").Write      NextRecipeEdition
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeName").Write         RecipeName
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeDescription").Write  RecipeDescription
    HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeID").Write           recipeID
  
    '從中間變量寫入到用戶歸檔
    If Insert_UA("@NOP::" &tagPrefix& "UA_Recipe") <> 0 Then
        Msgbox TranslateText("上載失敗!",2052)
        Exit Sub 
    End If

    '記錄Audit
    CreateOpMsg PrefixToName(tagPrefix), TranslateText("上載配方",2052)&recipeID , "", recipeID, sComments 
            
    '更新控件
    Call WriteRecipeNoToComboBox(DSN, "ComboRecipeList")
    ScreenItems("ComboRecipeList").text = RecipeNo
    ScreenItems("ComboEditionList").text = ""
    
    Msgbox TranslateText("上載成功並已保存",2052)

    ScreenItems("BT_New").Enabled=True
    ScreenItems("BT_Download").Enabled=True
    ScreenItems("BT_Delete").Enabled=True
    ScreenItems("ComboRecipeList").Enabled=True
    ScreenItems("BT_Save").Enabled=False  

End Sub

'*******************************************************************************************************************************************

'刪除配方
Sub DeleteRecipe(tagPrefix, DSN, objNameList)
    '--------------------------------------
    ' 刪除配方:
    ' 檢查需刪除的配方是否生產過,
    ' 生產過的配方將其IsDeleted字段置1,
    ' 之后查詢時不再顯示該配方,
    ' 打印報表時依然可以打印該配方;
    ' 未生產過的配方則被直接刪除。
    '-------------------------------------                                            
    
    '檢查是否選擇配方
    If ScreenItems("ComboRecipeList").text="" Or ScreenItems("ComboEditionList").text="" Then
        MsgBOX TranslateText("沒有選擇正確的配方!",2052)
        Exit Sub
    End If  
    
    '電子簽名
    Dim sComments
    If EsigDialog(sComments,False,"") <> 1 Then
        Exit Sub 
    End If
    
    '計算配方ID
    Dim RecipeID 
    RecipeID = ScreenItems("ComboRecipeList").text&"_"&ScreenItems("ComboEditionList").text
    
    '更新中間變量
    If Select_UA ("@NOP::" &tagPrefix& "UA_Recipe","Recipe_ID", RecipeID ) <> 0 Then
        MsgBOX TranslateText("讀取配方失敗!",2052)
        exit Sub
    end if

    '刪除配方
    If HMIRuntime.Tags("@NOP::" &tagPrefix& "M_IsProduced").Read = 1 Then
        HMIRuntime.Tags("@NOP::" &tagPrefix& "M_IsDeleted").Write 1
        If Update_UA ("@NOP::" &tagPrefix& "UA_Recipe", "Recipe_ID", RecipeID ) <> 0 Then
            Msgbox TranslateText("刪除配方失敗!",2052)
            exit sub
        end if
    Else
        If Delete_UA ("@NOP::" &tagPrefix& "UA_Recipe","Recipe_ID",RecipeID ) <> 0 Then
            Msgbox TranslateText("刪除配方失敗!",2052)
            exit sub
        end if 
    End If
    
    '記錄Audit
    CreateOpMsg PrefixToName(tagPrefix), TranslateText("刪除配方",2052)&recipeID , "", recipeID, sComments 
    
    '清空配方控件內容
    Dim objName
    For Each objName In objNameList
        ScreenItems(objName(0)).text = ""
    Next
    ScreenItems("Recipe_Name").text        = ""
    ScreenItems("Recipe_Description").text = ""
    ScreenItems("ComboRecipeList").text    = ""
    ScreenItems("ComboEditionList").clear 
    ScreenItems("ComboEditionList").text   = ""
    
    '更新控件中的配方編號
    Call WriteRecipeNoToComboBox(DSN, "ComboRecipeList")
        
    '設置按鈕狀態
    ScreenItems("BT_New").Enabled=True
    ScreenItems("BT_Download").Enabled=False
    ScreenItems("BT_Delete").Enabled=False

    Msgbox TranslateText("刪除成功",2052)
End Sub

'*******************************************************************************************************************************************

'獲取配方版本
Sub GetRecipeEdition(tagPrefix, DSN, objNameList)
    '------------------------------------------------
    ' 查詢數據庫中特定配方編號的版本,寫入到下拉控件。
    '------------------------------------------------
    '創建數據庫對象                
    Dim cnn,rs,cnnStr, SQL,i
    Set cnn = CreateObject("ADODB.Connection")
    Set rs = CreateObject("ADODB.Recordset") 
    
    '查詢特定配方編號的數據
    SQL = "select Recipe_Edition from "& DSN &" where Recipe_No='" & ScreenItems("ComboRecipeList").text & "' AND IsDeleted = 0"
    ConnectDatabase cnn,rs,SQL
    
    '將數據庫中存在的配方版本填入控件
    ScreenItems("ComboEditionList").clear
    If rs.RecordCount>0 Then
        rs.MoveFirst        
        For i = 1 To rs.RecordCount
            ScreenItems("ComboEditionList").AddItem rs("Recipe_Edition")
            rs.MoveNext
        Next
    End If
    '關閉數據庫連接
    rs.Close
    Set rs = Nothing
    cnn.Close
    Set cnn = Nothing
    
    '清空參數控件內容
    For Each objName In objNameList
        ScreenItems(objName(0)).text = ""
    Next
    
    '清空配方信息控件內容
    ScreenItems("Recipe_Name").text = ""
    ScreenItems("Recipe_Description").text = ""
    
    '設置按鈕狀態
    ScreenItems("BT_New").Enabled=True
    ScreenItems("BT_Delete").Enabled=False
    ScreenItems("BT_Save").Enabled=False
    ScreenItems("BT_Download").Enabled=False  
End Sub 

'*******************************************************************************************************************************************

'獲取配方
Sub GetRecipe(tagPrefix, DSN, objNameList)
    '----------------------------------------------
    ' 用配方編號和配方版本拼接得到配方ID,
    ' 用配方ID指定配方寫入到中間變量,
    ' 再將中間變量的內容寫入到控件去顯示。
    '----------------------------------------------
                                                                                                 
    '獲取配方ID
    Dim RecipeID
    RecipeID = ScreenItems("ComboRecipeList").text &"_"& ScreenItems("ComboEditionList").text
    
    '將配方從數據庫寫入到中間變量
    If Select_UA("@NOP::" &tagPrefix& "UA_Recipe","Recipe_ID",RecipeID) <> 0 Then
        Msgbox TranslateText("讀取配方失敗!",2052)
        Exit Sub
    End If
    
    '將配方從中間變量寫入到控件
    Dim objName 
    For Each objName In objNameList
        ScreenItems(objName(0)).text = HMIRuntime.Tags("@NOP::" &tagPrefix& "M_"&objName(1)).Read
    Next        
    
    '將配方信息從中間變量寫入到控件
    ScreenItems("Recipe_Name").text = HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeName").Read
    ScreenItems("Recipe_Description").text = HMIRuntime.Tags("@NOP::" &tagPrefix& "M_RecipeDescription").Read
    
    '設置按鈕狀態
    ScreenItems("BT_New").Enabled=True
    ScreenItems("BT_Delete").Enabled=True
    ScreenItems("BT_Save").Enabled=False
    ScreenItems("BT_Download").Enabled=True
End Sub

    

依賴函數

 上述代碼中依賴一些其他的函數。

TranslateText()

TranslateText()函數用於腳本內文本多語言翻譯,函數的設計和使用詳見:WinCC腳本內文本多語言化的一種方法

 

Check_LawlessChar()

輸入配方編號要求為字母和數據,Check_LawlessChar()函數通過正則表達式判斷輸入的字符是否符合要求,符合則返回1,不符合返回0。代碼如下:

Function Check_LawlessChar(strName)
    Dim regEx 
    Set regEx = New RegExp 
    regEx.Pattern = "^[a-z0-9A-Z]+$" 
    regEx.IgnoreCase = True 
    regEx.Global = True 
    Validate = regEx.test(strName) 
    Set regEx = Nothing 
    Check_LawlessChar = Not Validate
End Function

  

ConnectDatabase()

ConnectDatabase()函數用於簡化數據庫連接,代碼如下:

Function ConnectDatabase(cnn,rs,SQL)
    Dim cnnStr  
    cnnStr = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog="& HMIRuntime.Tags("@NOTP::@DatasourceNameRT").Read &";Data Source=" & HMIRuntime.Tags("@NOTP::@ServerName").Read & "\WINCC"
    cnn.ConnectionString = cnnStr
    cnn.CursorLocation = 3
    cnn.Open
    rs.Open SQL,cnn,1,3  
End Function

  

EsigDialog()和CreateOpMsg()

EsigDialog()和CreateOpMsg()分別用於生產電子簽名和記錄審計追蹤,詳見:WinCC的電子簽名與審計追蹤 2.0

 

PrefixToName()

審計追蹤需要記錄設備名稱,已知設備的變量前綴,可以得到對應的變量名稱,PrefixToName()就是為了實現這個作用,但要根據項目修改和添加一下代碼:

Function PrefixToName(Prefix)
    Select Case Trim(Prefix)
        Case "XX_"
            PrefixToName = TranslateText("設備1", 2052)
        Case "XX1_"
            PrefixToName = TranslateText("設備2", 2052)
        Case ""
            PrefixToName = "System"
        Case Else
            PrefixToName = Prefix
    End Select
End Function

  

畫面全局聲明區定義

將函數所需要的三個參數定義在畫面的全局聲明區中,在控件的腳本中就可以直接使用。

打開全局聲明區只需打開畫面中任意一個VBS編輯器,點擊下圖中紅色方框的按鈕,就可顯示全局聲明區。

 

全局聲明區代碼示例如下:

'數據庫名稱 
Dim DSN
DSN = "UA#XX_Recipe"

'控件名稱\數據庫字段名,變量名稱
Dim objNameList
objNameList = Array(_
    Array("i16_Set_VfFaninfeed", "i16_Set_VfFaninfeed"),_
    Array("i16_Set_VfFanPreheat", "i16_Set_VfFanPre-heat"),_
    Array("i16_Set_VfFan1Heat", "i16_Set_VfFan1#Heat"),_
    Array("i16_Set_VfFan1Cool", "i16_Set_VfFan1Cool"),_
    Array("i16_Set_VfFan2Cool", "i16_Set_VfFan2Cool"),_
    Array("Recipe_Setbeltspeed", "Recipe_Setbeltspeed"),_
    Array("Recipe_Setheat2", "Recipe_Setheat2"),_
    Array("Sv1Height_Upperlimit", "r32_Set_Sv1Height_Upperlimit"),_
    Array("Sv2Height_Upperlimit", "r32_Set_Sv2Height_Upperlimit"),_
    Array("Sv3Height_Upperlimit", "r32_Set_Sv3Height_Upperlimit") _
    )

'變量前綴
Dim TagPrefix
TagPrefix = "XX_"

  

操作批次的函數執行過程

點擊新建、保存、下載、上載、刪除按鈕以及查詢配方,執行流程如下:

 

 

 新建配方

新建按鈕中的腳本如下:

Sub OnClick(Byval Item) 
    NewRecipe TagPrefix, DSN, objNameList 
End Sub

  

點擊“新建”按鈕,調用NewRecipe()函數,將會執行以下操作:

  • 檢查“BT_Save”按鈕是否使能,判斷當前是否有正在編輯的配方沒保存,彈出提示框詢問用戶是否繼續操作;
  • 彈出對話框要求輸入配方編號、配方名稱和配方描述,檢查輸入的配方編號是否符合要求,和是否與現有的配方編號重復;
  • 清空界面中參數控件的值,向配方信息控件寫入用戶輸入的值;
  • 更新按鈕的使能狀態。

 

保存配方

保存按鈕中的腳本如下:

Sub OnClick(Byval Item) 
    SaveRecipe TagPrefix, DSN, objNameList 
End Sub

  

點擊“保存”按鈕,調用SaveRecipe()函數,將會執行以下操作:

  •  檢查是否有配方編號和配方版本;
  • 驗證電子簽名;
  • 查找數據庫中該配方編號的最大版本,計算得到下一配方版本;
  • 清空中間變量;
  • 向中間變量寫入界面上顯示的值;
  • 將中間變量的值保存到用戶歸檔;
  • 記錄審計追蹤;
  • 更新界面上配方版本控件的值;
  • 更新按鈕的使能狀態。

 

下載配方

下載按鈕中的腳本如下:

Sub OnClick(Byval Item)              
    DownloadRecipe TagPrefix, DSN, objNameList 
End Sub

  

點擊“下載”按鈕,調用DownloadRecipe()函數,將會執行以下操作:

  • 檢查是否選擇了一個配方;
  • 驗證電子簽名;
  • 通過配方編號和配方版本計算得到配方ID;
  • 用配方ID從用戶歸檔查詢配方值寫入到中間變量;
  • 將中間變量的寫入到外部變量;
  • 記錄審計追蹤;
  • 更新按鈕的使能狀態。

 

上載配方

上載按鈕中的腳本如下:

Sub OnClick(Byval Item)   
    Upload TagPrefix, DSN, objNameList 
End Sub

  

點擊“上載”按鈕,調用Upload()函數,將會執行以下操作:

  • 檢查“BT_Save”按鈕是否使能,判斷當前是否有正在編輯的配方沒保存,彈出提示框詢問用戶是否繼續操作;
  • 如果當前界面已選擇了配方,詢問用戶是否上載到當前配方編號;
  • 驗證電子簽名;
  • 如果需新建配方編號,彈出對話框要求輸入配方編號、配方名稱和配方描述,檢查輸入的配方編號是否符合要求,和是否與現有的配方編號重復;
  • 如果使用當前配方編號,則只需輸入新的配方描述;
  • 查找數據庫中該配方編號的最大版本,計算得到下一配方版本;
  • 把外部變量的值寫入到中間變量;
  • 把控件中的配方信息寫入到中間變量;
  • 把中間變量的值保存到用戶歸檔;
  • 記錄審計追蹤;
  • 更新配方編號和配方版本控件的值;
  • 更新按鈕的使能狀態。

 

刪除配方

刪除按鈕中的腳本如下:

Sub OnClick(Byval Item)
    DeleteRecipe TagPrefix, DSN, objNameList 
End Sub

  

點擊“刪除”按鈕,調用DeleteRecipe()函數,將會執行以下操作:

  • 檢查是否選擇了一個配方;
  • 驗證電子簽名;
  • 通過配方編號和配方版本計算得到配方ID;
  • 用配方ID從用戶歸檔查詢配方值寫入到中間變量;
  • 檢查“M_IsProduced”是否為1,判斷該配方是否被生產過,以確定是否要刪除該配方;
  • 如果“M_IsProduced”是否為1,則把“M_IsDeleted”變量置1,更新用戶歸檔中的值;
  • 如果“M_IsProduced”是否為0;則直接在用戶歸檔中刪除該配方;
  • 記錄審計追蹤;
  • 更新配方編號控件的值。
  • 更新按鈕的使能狀態。

 

編輯配方

編輯配方有以下2點要求:

  • 能告知被編輯的參數的最大值和最小值,輸入的值不能超過最大值和最小值;
  • 編輯在線配方值時要求有電子簽名和審計追蹤。

要滿足上述要求,就不能直接在Wincc的輸入輸出域控件里輸入值,需要設計一個輸入界面顯示參數的最大值和最小值,用腳本檢查輸入的是否在范圍之內,編輯在線配方值時還要進行電子簽名。輸入界面如下,點擊數值控件時以對話框形式彈出,類似於屏幕鍵盤(以下稱作屏幕鍵盤界面)。

 

屏幕鍵盤界面是個通過畫面窗口調用的畫面,這個畫面窗口平常隱藏,需要輸入配方值時通過調用函數顯示畫面窗口,編輯數據庫配方值和在線配方值用的是不同的調用函數。

編輯數據庫配方值時調用KeyboardDispScreen()函數顯示屏幕鍵盤界面,示例如下:

Sub OnClick(ByVal Item)     
    KeyboardDispScreen AccessPath, Item.ObjectName, 0, 50, "配方變量:進瓶風機頻率設定(Hz)"
End Sub

  

編輯在線配方值時調用KeyboardDispTag()函數顯示屏幕鍵盤界面,示例如下:

Sub OnClick(Byval Item)    
    KeyboardDispTag parent.TagPrefix, "i16_Set_VfFaninfeed", 0, 50, Item.TooltipText 
End Sub

  

屏幕鍵盤界面通過位於根畫面的畫面窗口控件引用,畫面窗口控件名為“Keyboard”。屏幕鍵盤界面中有四個隱藏的文本控件,分別用於存儲變量前綴、變量名、畫面路徑、控件名稱,然后屏幕鍵盤界面里的代碼就可以使用這幾個值。KeyboardDispScreen()函數只傳遞畫面路徑和控件名稱,KeyboardDispTag()函數只傳遞變量前綴和變量名。

KeyboardDispScreen()函數代碼如下:

'---------------------------------------------------------------------------------
' ScreenPath:調用函數的控件所在的畫面路徑
' ItemName  :調用控件的控件名
' MinVal    :最小值
' MaxVal    :最大值
' TagNote   :被編輯的參數的描述
'---------------------------------------------------------------------------------
Function KeyboardDispScreen(ScreenPath,ItemName,MinVal,MaxVal,TagNote)
    Dim objScrKeyboard,objScrWindow
    Set objScrKeyboard=HMIRuntime.Screens(BaseScreenName).ScreenItems("Keyboard") 
    objScrKeyboard.CaptionText=TagNote'TagName
    objScrKeyboard.Visible=True
    Set objScrWindow = objScrKeyboard.Screen
    objScrWindow.ScreenItems("ScreenPath").Text = ScreenPath
    objScrWindow.ScreenItems("ItemName").Text = ItemName
    objScrWindow.ScreenItems("ET_MinValue").OutputValue = MinVal
    objScrWindow.ScreenItems("ET_MaxValue").OutputValue = MaxVal
End Function

  

KeyboardDispTag()函數代碼如下:

'---------------------------------------------------------------------------------
' Prefix    :變量的前綴
' ItemName  :變量名稱
' MinVal    :最小值
' MaxVal    :最大值
' TagNote   :被編輯的變量的描述
'---------------------------------------------------------------------------------
Function KeyboardDispTag(Prefix,TagName,MinVal,MaxVal,TagNote)
    Dim objScrKeyboard,objScrWindow
    Set objScrKeyboard=HMIRuntime.Screens(BaseScreenName).ScreenItems("Keyboard") 
    objScrKeyboard.CaptionText=TagNote'TagName
    objScrKeyboard.Visible=True
    Set objScrWindow = objScrKeyboard.Screen
    objScrWindow.ScreenItems("Prefix").Text = Prefix
    objScrWindow.ScreenItems("TagName").Text = TagName
    objScrWindow.ScreenItems("ET_MinValue").OutputValue = MinVal
    objScrWindow.ScreenItems("ET_MaxValue").OutputValue = MaxVal
End Function

 

存儲變量前綴、變量名、畫面路徑、控件名稱的四個控件獲得值后,就會腳本寫入到全局聲明的變量,便於之后使用。

 

 “確認”按鈕中的代碼如下,如果控件名稱(ItemName)變量不為空,就執行寫入到界面控件的腳本,其中第36行至53行代碼會將調用控件同一個畫面的某些按鈕設為啟用或禁用。

如果變量名稱(TagName)變量不為空,驗證電子簽名后就會執行寫入到變量的代碼。

Sub OnClick(Byval Item)                                                                                                                                                   
    Dim objScrKeyboard, InputValue, MaxValue, MinValue, OutputValue
    ' 獲取畫面窗口控件
    Set objScrKeyboard = Parent

    '檢查輸入內容是否為空
    If Trim(ScreenItems("Txt_Value").Text)="" Then
        objScrKeyboard.Visible=False
        Exit Sub
    End If 
    
    '獲取數據值和限制值
    InputValue=CSng(ScreenItems("Txt_Value").Text)
    MaxValue=CSng(ScreenItems("ET_MaxValue").OutputValue)
    MinValue=CSng(ScreenItems("ET_MinValue").OutputValue)
    
    '檢查值范圍
    If InputValue>MaxValue Then
        OutputValue=MaxValue
    Elseif InputValue<MinValue Then
        OutputValue=MinValue
    Else
        OutputValue=InputValue
    End If
    
    
    If ItemName <> "" Then 
        Dim objScrEdit
        '獲取畫面對象
        Set objScrEdit=HMIRuntime.Screens(ScreenPath) 
        
        '寫入值
        objScrEdit.ScreenItems(ItemName).text = CStr(OutputValue)
        
        '更新按鈕狀態
        Dim EnableButtuns, DisableButtuns, ButttunName
        EnableButtuns = Array("BT_Save")
        DisableButtuns = Array("BT_Download", "BT_Delete")
        Dim ItemNameTemp
        For Each ItemNameTemp In objScrEdit.ScreenItems
            If ItemNameTemp.Type = "HMIButton" Then
                For Each ButttunName In EnableButtuns
                    If ItemNameTemp.ObjectName = ButttunName Then
                        ItemNameTemp.Enabled = True
                    End If
                Next 
                For Each ButttunName In DisableButtuns
                    If ItemNameTemp.ObjectName = ButttunName Then
                        ItemNameTemp.Enabled = False
                    End If
                Next 
            End If
        Next
    End If
    
    If TagName <> "" Then
        '電子簽名,寫入值
        Dim TagNote
        TagNote = parent.CaptionText
        TagNewValueES PrefixToName(Prefix), TagNote, Prefix & TagName, OutputValue 
        'HMIRuntime.Tags(Prefix & TagName).Write OutputValue
    End If
    objScrKeyboard.Visible=False
    
End Sub

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM