WinCC的電子簽名與審計追蹤 2.0

之前寫過一篇WinCC的電子簽名與審計追蹤,在那篇文章中使用報警操作記錄生成審計追蹤,後來測試VB腳本執行的情況,發現審計追蹤中缺少執行該操作的用戶名和計算機名,用C腳本執行倒是沒有問題。在本文中再補充一個用InserAuditEntryNew生成審計追蹤的方法,並且不再把電子簽名和審計追蹤做在一個函數裏,將電子簽名和審計追蹤分成兩個函數分別執行可以更靈活。

 目錄

 

生成審計追蹤的方法


 用腳本向Audit中添加記錄有兩種方法,一種方法是用WinCC提供的InserAuditEntryNew函數寫入,另一種方法是生成屬於“操作員輸入消息”類型的報警消息,該報警消息同時也會記錄到Audit中。

WinCC中的某些操作本身就會生成審計追蹤記錄,例如啓動或關閉系統、登錄用戶等,這些操作生成的Audit記錄的TargetName列是操作內容,而使用InserAuditEntryNew函數生成的記錄在TargetName列的內容默認爲VBScripting Runtime或CScripting Runtime,而操作內容只能記錄在Reason列處。

WinCC的電子簽名與審計追蹤一文中介紹瞭如何用“操作員輸入消息”類型的報警生成把操作內容記錄在TargetName列的審計追蹤消息,之前以C腳本測試過這個方法可以達到要求,後來用VB腳本測試,審計追蹤中ApplicationUser和ComputerName中是空的,即沒有操作人和操作終端名稱。如果要用VB腳本執行,這種方法是不能滿足審計追蹤要求的。在本文中再介紹使用InserAuditEntryNew函數生成審計追蹤的方法。

 

VB腳本的電子簽名和審計追蹤


VB腳本的InsertAuditEntryNew函數

函數原型:

InsertAuditEntryNew(strOldValue, strNewValue, strOpComments, iComment)

參數:

參數 描述
strOldValue 舊值
strNewValue 新值
strOpComments 註釋
iComment

0:將strOpComments參數的值作爲註釋記錄到audit trial中

1:顯示一個對話框,將對話框中輸入的內容的作爲註釋記錄到audit trial中

返回值:

  • 整型值,含義未知,沒有找到對此說明的官方文檔。

 

VB腳本的審計追蹤

把InserAuditEntryNew函數再封裝爲CreateOpMsg,添加一些參數。在VB全局腳本的項目模塊中建立該函數。

 函數原型:

Function CreateOpMsg(sSource, sInputMsg, OldValue, NewValue, sComments)

代碼:

'----------------------------------------------------------------------------
' 描述:
'   該函數向audit數據庫中插入一條審計追蹤記錄,
'   在數據庫的reason列寫入"sSource: sInputMsg; sComments"字符串,
'   由三個字符串參數拼接而成。
' 參數:
'   sSource    :該字符串表示這條記錄的來源或類型
'   sInputMsg  :該字符串表示這條記錄的操作說明
'   OldValue   :舊值
'   NewValue   :新值
'   sComments  :用戶註釋
' 返回值:
'   與InsertAuditEntryNew函數返回值相同。
'----------------------------------------------------------------------------
Function CreateOpMsg(sSource, sInputMsg, OldValue, NewValue, sComments)
    CreateOpMsg = InsertAuditEntryNew(CStr(OldValue), CStr(NewValue), sSource&": "&sInputMsg&"; "&sComments, 0)
'--------------------------------
' 以下爲通過報警生成審計追蹤的代碼
'--------------------------------
'   '創建操作員消息
'    dim myAlarm
'    Set myAlarm = HMIRuntime.Alarms(12508142) 
'    MyAlarm.State = 5
'    '--------------------------
'    'State  Alarm Log Status
'    '1      Came In
'    '2      Went Out
'    '5      Came in and comment
'    '6      Gone and comment
'    '--------------------------
'    myAlarm.Comment = sComments
'    myAlarm.UserName = HMIRuntime.Tags("@NOP::@CurrentUser").Read
'    myAlarm.ProcessValues(2) = OldValue
'    myAlarm.ProcessValues(3) = NewValue
'    myAlarm.ProcessValues(10) = sSource
'    myAlarm.ProcessValues(7) = sInputMsg
'    MyAlarm.Create
End Function

  

VB腳本的電子簽名

在VB全局腳本的項目模塊中建立以下函數。

函數原型:

Function EsigDialog(Byref sComments, Byval bForcecomment, Byval sUserName)

代碼:

'-----------------------------------------------------------------------------------------
' 描述:
'   執行該函數將彈出電子簽名對話框,
'   並要求輸入註釋,可以設置是否要求強制註釋,
'   註釋內容通過sComments參數返回,
'   如果未給sUserName參數寫入字符串,則使用當前登錄的賬戶執行電子簽名,
'   如果給sUserName參數寫入了字符串,則將該字符串作爲用戶名執行電子簽名。
' 參數:
'   sComments     :引用一個字符串變量,註釋內容將會寫入到該變量中。
'   bForcecomment :
'                   False:可以不輸入註釋,即註釋爲空;
'                   True :必須輸入註釋。
'   sUserName     :執行電子簽名的用戶名,如果該參數爲空字符串,則使用當前登錄的賬戶執行電子簽名。
' 返回值:
'   -1:函數執行遇到錯誤
'    1:電子簽名通過
'    2:用戶取消了電子簽名
'    3:三次電子簽名未通過
'------------------------------------------------------------------------------------------
Function EsigDialog(Byref sComments, Byval bForcecomment, Byval sUserName)
    Dim sDisplayedUser, sDomain
    sDomain = ""  '如果加入了域,在此填寫域名
    '如果sUserName參數不爲空,則使用提供的用戶名做電子簽名,否則使用當前登錄的賬戶做電子簽名
    sUserName = Trim(sUserName)
    if sUserName <> "" then  
        sDisplayedUser = sUserName
    Else      
        sUserName      = HMIRuntime.Tags("@NOP::@CurrentUser").Read
        sDisplayedUser = HMIRuntime.Tags("@NOP::@CurrentUserName").Read
        If sUserName = "" Then
            Select Case HMIRuntime.Language
                Case 2052
                    Msgbox "用戶未登錄,請登錄後再執行!"
                Case 1033
                    Msgbox "Please log in before executing!"
                Case Else
                    Msgbox "Please log in before executing!"
            End Select
            EsigDialog = -1
            Exit Function
        End If
    End If

    '電子簽名
    Dim myEsig
    Dim ret
    Set myEsig = CreateObject("CCEsigDlg.ESIG")
    '強制註釋
    myEsig.forcecomment = bForcecomment
    ret = myEsig.showDialog(sUserName,sDisplayedUser,sDomain, HMIRuntime.Language, sComments)
    EsigDialog = ret
End Function

 

VB腳本的電子簽名和審計追蹤示例

'---------------------------------------------------------------
' 設定一個變量的新值,完成電子簽名後,將修改的值記錄到審計追蹤
'---------------------------------------------------------------'
Dim OldValue, NewValue
Dim sComments
NewValue = inputbox("設定加熱溫度")
if Not IsNumeric(NewValue) then
    msgbox ("只能輸入數字!")
else
    if EsigDialog(sComments, False, "") = 1 then
        OldValue = HMIRuntime.Tags("tag1").Read
        HMIRuntime.Tags("tag1").Write NewValue
        Call CreateOpMsg("修改變量", "設定加熱溫度" , OldValue, NewValue, sComments)
    end if
end if

  

VB腳本的寫入變量新值的封裝函數

在通過電子簽名後,向一個變量寫入新值,並記錄到審計追蹤,這個操作在WinCC中經常會使用。可以看到前面的代碼稍有複雜,對這個過程再做一次封裝在使用時會更加方便。

函數原型:

Function TagNewValueES(sSource, sInputMsg, sTagName, NewValue)

代碼:

'------------------------------------------------------------------------------------
' 描述:
'   執行電子簽名,通過後向一個變量中寫入新值,並將變量的新值和舊值記錄到審計追蹤,
'   在審計追蹤的reason列中寫入"sSource: sTagName: sInputMsg; sComments"字符串。
' 參數:
'   sSource    :該字符串表示這條記錄的來源或類型
'   sInputMsg  :該字符串表示修改變量的操作說明
'   sTagName   :變量名
'   NewValue   :新值
' 返回值:
'   -1:函數執行遇到錯誤
'    1:電子簽名通過
'    2:用戶取消了電子簽名
'    3:三次電子簽名未通過
'--------------------------------------------------------------------------------------

Function TagNewValueES(sSource, sInputMsg, sTagName, NewValue)
    Dim OldValue
    Dim sComments
    Dim iRet
    iRet = EsigDialog(sComments, False, "")
    if iRet = 1 then
        OldValue = HMIRuntime.Tags( sTagName ).Read
        HMIRuntime.Tags( sTagName ).Write NewValue
        Call CreateOpMsg(sSource, sTagName&": "&sInputMsg , OldValue, NewValue, sComments)
    end if
    TagNewValueES = iRet
End Function

  

C腳本的電子簽名和審計追蹤


C腳本的InsertAuditEntryNew函數

函數原型:

long int InsertAuditEntryNew(char* szOldValue, char* szNewValue, char* szOpComments, BOOL iComment, char* szReturnBuffer)

參數:

參數 描述
szOldValue 舊值
szNewValue 新值
szOpComments 註釋
iComment

0:將strOpComments參數的值作爲註釋記錄到audit trial中

1:顯示一個對話框,將對話框中輸入的內容的作爲註釋記錄到audit trial中

szReturnBuffer

引用一個字符串數組,含義不明,官方文檔未對此說明

返回值:

  • 整型值,含義未知,沒有找到對此說明的官方文檔。

 

C腳本的審計追蹤

把InserAuditEntryNew函數再封裝爲CreateOpMsg,添加一些參數。在C全局腳本的項目函數中建立該函數。

函數原型:

long int CreateOpMsg(char* szSource, char* szMsg, char* szOldValue, char* szNewValue, char *szComments)

代碼:

#include "apdefap.h"
/*---------------------------------------------------------------------------------------------------
* 描述:
*   該函數向audit數據庫中插入一條審計追蹤記錄,
*   在數據庫的reason列寫入"szSource: szMsg; szComments"字符串,
*   由三個字符串參數拼接而成。
* 參數:
*   szSource    :該字符串表示這條記錄的來源或類型
*   szMsg       :該字符串表示這條記錄的操作說明
*   szOldValue  :舊值
*   szNewValue  :新值
*   szComments  :用戶註釋
* 返回值:
*   與InsertAuditEntryNew函數返回值相同。
*----------------------------------------------------------------------------------------------------*/
long int CreateOpMsg(char* szSource, char* szMsg, char* szOldValue, char* szNewValue, char *szComments)
{
    char szCommentsBuffer[1024] = "";
    char szReturnBuffer[1024] = "";
    sprintf(szCommentsBuffer,"%s: %s; %s", szSource, szMsg, szComments);
    return InsertAuditEntryNew(szOldValue,szNewValue,szCommentsBuffer,0,szReturnBuffer);    //Return-Type: long int 
}

  

C腳本的電子簽名

在C全局腳本的項目函數中建立以下函數。

函數原型:

int EsigDialog(char* szComments,BOOL bForcecomment, char* szUserName)

代碼:

#include "apdefap.h"
/*---------------------------------------------------------------------------------------------
* 描述:
*   執行該函數將彈出電子簽名對話框,
*   並要求輸入註釋,可以設置是否要求強制註釋,
*   註釋內容通過szComments參數返回,
*   如果未給szUserName參數寫入字符串,則使用當前登錄的賬戶執行電子簽名,
*   如果給szUserName參數寫入了字符串,則將該字符串作爲用戶名執行電子簽名。
* 參數:
*   szComments     :引用一個字符串變量,註釋內容將會寫入到該變量中。
*   bForcecomment  :
*                    False:可以不輸入註釋,即註釋爲空;
*                    True :必須輸入註釋。
*   szUserName     :執行電子簽名的用戶名,如果該參數爲空字符串,則使用當前登錄的賬戶執行電子簽名。
* 返回值:
*   -1:函數執行遇到錯誤
*    1:電子簽名通過
*    2:用戶取消了電子簽名
*    3:三次電子簽名未通過
*-----------------------------------------------------------------------------------------------*/
int EsigDialog(char* szComments,BOOL bForcecomment, char* szUserName)
{
    char *Domain = "";  //如果加入了域,在此填寫域名
    char *szDisplayedUser = "";
    int iRet = 0;
    VARIANT vtComment;
    __object* EsigDlg;
    char szUserNameBuffer[256]="";
    char *strBegin = NULL;
    char *strEnd   = NULL;

    /* 刪除szUserName字符串兩端空格 */
    if (szUserName != NULL ){
        strBegin = szUserName;
        while(*strBegin && isspace(*strBegin))
            strBegin++; //如果是空格,首地址往前移一位,如果不是,則跳過該循環
        strncpy(szUserNameBuffer,strBegin,255);
        strEnd = szUserNameBuffer + (strlen(szUserNameBuffer) - 1);
        while(*strEnd && isspace(*strEnd))
            *strEnd-- = '\0'; //如果是空格,末地址往前移一位,並賦結束符
    }
    /* 如果szUserNameBuffer參數不爲空,則使用提供的用戶名做電子簽名,否則使用當前登錄的賬戶做電子簽名 */
    if (strlen(szUserNameBuffer)>0) {
        szUserName = szUserNameBuffer;
        szDisplayedUser = szUserName;
    } else {
        szUserName      = GetTagChar("@NOP::@CurrentUser");  //Return-Type: char* 
        szDisplayedUser = GetTagChar("@NOP::@CurrentUserName");  //Return-Type: char* 
        /* 判斷用戶是否登錄 */
        if (strlen(szUserName) == 0) {
            switch (GetLanguage()) {
                case 2052:
                    MessageBox(NULL,"用戶未登錄,請登錄後再執行!","Error",MB_SYSTEMMODAL|MB_OK);
                    break;
                case 1033:
                default:
                    MessageBox(NULL,"Please log in before executing!","Error",MB_SYSTEMMODAL|MB_OK);
                    break;
            } 
            return -1;
        }
    }
    
    /* 電子簽名 */
    EsigDlg = __object_create("CCESigDlg.ESIG");
 
    if (!EsigDlg) {
        printf("Failed to create Picture Object\n");
        return -1;
    }
 
    EsigDlg->forcecomment = bForcecomment; 
    iRet = EsigDlg->ShowDialog(szUserName,szDisplayedUser,Domain,GetLanguage(),&vtComment);
    __object_delete(EsigDlg);
    /* 提取註釋 */
    if (szComments != NULL)
        sprintf(szComments,"%ls",vtComment.u.bstrVal);
    VariantClear(&vtComment);
    
    return iRet;
}

  

C腳本的電子簽名和審計追蹤示例

#include "apdefap.h"
void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)
{

// WINCC:TAGNAME_SECTION_START
// syntax: #define TagNameInAction "DMTagName"
// next TagID : 1
// WINCC:TAGNAME_SECTION_END

// WINCC:PICNAME_SECTION_START
// syntax: #define PicNameInAction "PictureName"
// next PicID : 1
// WINCC:PICNAME_SECTION_END

/*------------------------------------------------------------------
* 設定一個變量的新值,完成電子簽名後,將修改的值記錄到審計追蹤
*------------------------------------------------------------------*/
    char szComments[2014]="";
    double OldValue, NewValue;
    char sOldValue[512]="";
    char sNewValue[512]="";
    NewValue = 97;
    if (EsigDialog (szComments, FALSE, "") == 1 ) {
        OldValue = GetTagDouble("tag1");
        SetTagDouble("tag1", NewValue);
        sprintf(sOldValue,"%f",OldValue);
        sprintf(sNewValue,"%f",NewValue);
        CreateOpMsg("修改變量", "設定加熱溫度", sOldValue, sNewValue, szComments);
    }
}

  

C腳本的寫入變量新值的封裝函數

在通過電子簽名後,向一個變量寫入新值,並記錄到審計追蹤,這個操作在WinCC中經常會使用。可以看到前面的代碼稍有複雜,對這個過程再做一次封裝在使用時會更加方便。

向數值類型的變量寫入新值

函數原型:

int DoubleTagNewValueES(char* szSource, char* szMsg, char* szTagName, double dNewValue)

代碼:

#include "apdefap.h"
/*---------------------------------------------------------------------------------------
* 描述:
*   執行電子簽名,通過後向一個變量中寫入新值,並將變量的新值和舊值記錄到審計追蹤,
*   在審計追蹤的reason列中寫入"szSource: szTagName: szMsg; szComments"字符串。
* 參數:
*   szSource    :該字符串表示這條記錄的來源或類型
*   szMsg       :該字符串表示修改變量的操作說明
*   szTagName   :變量名
*   dNewValue   :新值
* 返回值:
*   -1:函數執行遇到錯誤
*    1:電子簽名通過
*    2:用戶取消了電子簽名
*    3:三次電子簽名未通過
*-----------------------------------------------------------------------------------------*/
int DoubleTagNewValueES(char* szSource, char* szMsg, char* szTagName, double dNewValue)
{
    char szComments[2014]="";
    double dOldValue;
    char szOldValue[512]="";
    char szNewValue[512]="";
    char szbuffer[512]="";
    int iRet = EsigDialog (szComments, FALSE, "");
    if ( iRet == 1 ) {
        dOldValue = GetTagDouble(szTagName);
        SetTagDouble(szTagName, dNewValue);
        sprintf(szOldValue,"%f",dOldValue);
        sprintf(szNewValue,"%f",dNewValue);
        sprintf(szbuffer,"%s: %s", szTagName, szMsg);
        CreateOpMsg(szSource, szbuffer, szOldValue, szNewValue, szComments);
    }
    return iRet;
}

  

向字符串類型的變量寫入新值

函數原型:

int StrTagNewValueES(char* szSource, char* szMsg, char* szTagName, char* szNewValue)

代碼:

#include "apdefap.h"
/*-------------------------------------------------------------------------------------
* 描述:
*   執行電子簽名,通過後向一個變量中寫入新值,並將變量的新值和舊值記錄到審計追蹤,
*   在審計追蹤的reason列中寫入"szSource: szTagName: szMsg; szComments"字符串。
* 參數:
*   szSource    :該字符串表示這條記錄的來源或類型
*   szMsg       :該字符串表示修改變量的操作說明
*   szTagName   :變量名
*   szNewValue  :新值
* 返回值:
*   -1:函數執行遇到錯誤
*    1:電子簽名通過
*    2:用戶取消了電子簽名
*    3:三次電子簽名未通過
*--------------------------------------------------------------------------------------*/
int StrTagNewValueES(char* szSource, char* szMsg, char* szTagName, char* szNewValue)
{
    char szComments[2014]="";
    char szOldValue[512]="";
    char szbuffer[512]="";
    int iRet = EsigDialog (szComments, FALSE ,"");
    if (iRet == 1 ) {
        sprintf(szOldValue,"%s", GetTagChar(szTagName) );
        SetTagChar(szTagName, szNewValue);
        sprintf(szbuffer,"%s: %s", szTagName, szMsg);
        CreateOpMsg(szSource, szbuffer, szOldValue, szNewValue, szComments);
    }
    return iRet;
}

 

查看Audit記錄

WinCC提供了AuditViewer控件查看Audit記錄,該控件顯示的示例如下。

 

 AuditViewer控件會顯示Audit數據庫中的所有列,不能通過配置隱藏某些不需要的列。下面提供一種參考方法,通過代碼查詢Audit數據顯示到表格控件中。

該方法是用VBS查詢數據庫,把查詢到的數據顯示到MSHFlexGrid控件,控件名爲Grid。查詢按鈕中的代碼見下方。

 

Sub OnClick(Byval Item)                                                                                                                                            
	'獲取篩選時間
	Dim timeFilter
	Dim BeginTime, EndTime
	BeginTime=DateValue(ScreenItems("BeginDate").value)+TimeValue(ScreenItems("Begintime").value)
	EndTime=DateValue(ScreenItems("EndDate").value)+TimeValue(ScreenItems("Endtime").value)
	If Datediff("s",BeginTime,EndTime) <= 0 Then
		Msgbox TranslateText("時間選擇不正確!", 2052)
		Exit Sub
	End If
	timeFilter =  " Dateadd(hh,[TimeZoneOffset],[DateTime])>= '"&BeginTime& ".000' AND Dateadd(hh,[TimeZoneOffset],[DateTime])<= '"&EndTime&".000' "
	timeFilter = Replace(timeFilter,"/","-")

	Dim cnn, rs,cnnStr, SQL, i
    Set cnn = CreateObject("ADODB.Connection")
    Set rs = CreateObject("ADODB.Recordset")
    
    '查詢Audit的數據庫名稱  
    Dim DatasourceNameRT, DatasourceNameRC, auditDataBase
    DatasourceNameRT = HMIRuntime.Tags("@NOP::@DatasourceNameRT").Read
    DatasourceNameRC = Left(DatasourceNameRT, Len(DatasourceNameRT)-1)
    cnnStr = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog="&DatasourceNameRC&";Data Source=" & HMIRuntime.Tags("@NOP::@ServerName").Read & "\WINCC"
    cnn.ConnectionString = cnnStr
    cnn.CursorLocation = 3
    cnn.Open
	SQL = "select DBName from [CC_Audit_SelectionsRT]"
	rs.open SQL, cnn, 1, 3
	If rs.EOF Then
		Msgbox TranslateText("Audit數據庫不存在!", 2052)
		Exit Sub
	Else
		auditDataBase = rs("DBName")
	End If
	rs.close
	cnn.Close
	
	'查詢Audit數據庫
   	cnnStr = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog="&auditDataBase&";Data Source=" & HMIRuntime.Tags("@NOP::@ServerName").Read & "\WINCC"
    cnn.ConnectionString = cnnStr
    cnn.CursorLocation = 3
    cnn.Open
    
    Dim table,EOF_flag
    Dim GridControl
    Set GridControl = ScreenItems("Grid")
    table = "CL_TRAILDATA"
    'SQL = "select SWITCHOFFSET([DateTime],[TimeZoneOffset]*60) as DateTime,[ApplicationUser],[ComputerName],[TargetName],[OldValue],[NewValue],[Reason] from "&table&" where "&timeFilter
    SQL = "select Dateadd(hh,[TimeZoneOffset],[DateTime]) as DateTime,[ApplicationUser],[ComputerName],[TargetName],[OldValue],[NewValue],[Reason] from "&table&" where "&timeFilter & "ORDER BY DateTime DESC"
    'HMIRuntime.Trace SQL&vbNewline
    rs.open SQL, cnn, 1, 3
	EOF_flag = rs.EOF
	GridControl.Clear
    GridControl.Recordset = rs
   	For i=1 To rs.RecordCount
   		GridControl.TextMatrix(i,0) = i
   	Next 
   	rs.close
   	
   	If Not EOF_flag Then
	    GridControl.ColWidth(1) = 3000
	    GridControl.ColWidth(2) = 1500
	    GridControl.ColWidth(3) = 2000
	  	GridControl.ColWidth(4) = 5000
	  	GridControl.ColWidth(5) = 4000
	  	GridControl.ColWidth(6) = 4000
	  	GridControl.ColWidth(7) = 8000
	End If

  	cnn.Close
    
End Sub

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章