WinCC 7. Лог действий оператора
-
-
15.03.2016 #88315
Николай
КлючникMarrenoloth
Коллеги, добрый день!
Может ли кто-нибудь показать как интегрировать скрипт логирования действий оператора в WinCC 7.
Сам скрипт и описание тут: dfpd.siemens.ru/
Описание создаваемых сообщений там тоже есть.
Проблема в моем низком уровне знаний WinCC. Может кто-нибудь расписать как это правильно сделать или подключиться по удаленке и потыкать меня мышью в монитор? Готов эти знания после получения выложить мануалом сюда.Exactamente
А в чём именно вопрос? Смотрите, функция n_GCreateMyOperationMsg, из первого листинга, создаёт пользовательское сообщение. По сути просто обвязка для винсисёвой GCreateMyOperationMsg. На любое событие, которое нужно логировать, вешается скрипт (в свойствах объекта в Graphics Designer’e, вторая вкладка), в котором помимо самих действий вызывается эта функция со всеми нужными параметрами (из не совсем неочевидного там только код сообщения, который передаётся вторым аргументом ‘DWORD dwMsgNum’, винсисёвые заморочки – нужно предварительно насоздавать в AlarmLogging’e шаблоны сообщений, последний скриншот в том посте). Собственно, второй и третий листинги – как раз те скрипты на событие с примерами вызова этой функции.Marrenoloth
Так вопросов два, по большому счету:
1. Куда положить сам скрипт (который обвязка)?
2. Как связать значения из скрипта с текстом сообщения в WinCC 7.3? Там совершенно другое окно свойств и как привязывать к нему параметры, непонятно. Ну и сам формат параметров хотелось бы понять. Хоть где почитать про это?Exactamente
Соврал, обвязка не для GCreateMyOperationMsg, а для MSRTCreateMsgInstanceWithComment. Собственно, всё интересное про неё можно почерпнуть из хелпа к ODK.
1. Скрипт положить в ProjectFolderLibrary (можно во вложенный каталог, например ProjectFolderLibraryOperationLogging). Запустить C-Sript Editor из WinCC Explorer’a, не открывая никакие скрипты в меню выбрать Options >, Regenerate Header, потом Options >, Compile All Functions. Слева в дереве появится скрипт: Project Functions >, n_GCreateMyOperationMsg (или Project Functions >, OperationLogging >, n_GCreateMyOperationMsg – если во вложенный каталог положили).
2. WinCC Explorer >, Alarm Logging – создаёте тексты сообщений в Message Blocks. Класс и тип влияют на их отображение/поведение (фильтрация, удержание аларма при уходе аварийного состояния и т.п., всё как везде). Вместо, например, 1%s или 2%d берётся строковое или числовое значение (%s, %d и т.п. работают как в printf’е) с соответствующим индексом из массива mtTextValue структуры MSG_RTDATA_INSTANCECOMMENT_STRUCT, которая передаётся в ф-ю MSRTCreateMsgInstanceWithComment (ещё подробнее почитать можно, опять же, в мануале на ODK). В случае конкретно этой функции смотрите под каким индексом что будет в блоке INITIALIZATION PROCESS VALUE BLOCKS 1..10- [+] тыц
- //======================================
// INITIALIZATION PROCESS VALUE BLOCKS 1..10
//======================================
GetComputerNameA(szComputerName, &dwBufSize),
// sprintf(szTmp, ‘Computername = %s rn’, szComputerName),
strncpy ( MsgCreateEx.mtTextValue[0].szText, szComputerName, sizeof (MsgCreateEx.mtTextValue[0].szText) – 1), // Computer Name
// printf(‘Start n_GCreateMyOperationMsg rn’),
szCurrentUser = GetTagChar(‘@local::@CurrentUser’),
strncpy ( MsgCreateEx.mtTextValue[1].szText, szCurrentUser, sizeof (MsgCreateEx.mtTextValue[1].szText) – 1), // Current User Name
MsgCreateEx.wPValueUsed = (WORD)(MsgCreateEx.wPValueUsed | 0x000C),
MsgCreateEx.dPValue[2] = doValueOld, // old value
MsgCreateEx.dPValue[3] = doValueNew, // new value
strncpy ( MsgCreateEx.mtTextValue[4].szText, lpszPictureName, sizeof (MsgCreateEx.mtTextValue[4].szText) – 1), // lpszPictureName
strncpy ( MsgCreateEx.mtTextValue[5].szText, lpszObjectName, sizeof (MsgCreateEx.mtTextValue[5].szText) – 1), // lpszObjectName
strncpy ( MsgCreateEx.mtTextValue[6].szText, lpszTagName, sizeof (MsgCreateEx.mtTextValue[6].szText) – 1), // lpszTagName
strncpy ( MsgCreateEx.mtTextValue[7].szText, lpszActionName, sizeof (MsgCreateEx.mtTextValue[7].szText) – 1), // lpszActionName
strncpy ( MsgCreateEx.mtTextValue[8].szText, lpszObjectName1, sizeof (MsgCreateEx.mtTextValue[8].szText) – 1), // lpszObjectName1
Не знаю, как модераторы отнесутся к ссылкам на вражеские ресурсы и неточтобысовсемужлегальныематериалы, которые там выложены, поэтому – на запрос ‘wincc odk скачать’ в гугле вторая ссылка 🙂
Marrenoloth писал(а): в WinCC 7.3? Там совершенно другое окно свойствТут не подскажу, 7.3 в глаза не видел
Не думаю, что там радикально переделали. Ищите Events. В версиях попроще оно должно выглядеть как-то так s52.radikal.ru/i137/1305/94/b712ccb9fb71.jpg , если тут 7.3, то там оно не сильно отличается вроде бы
Marrenoloth
Спасибо, вечером буду копать! А ссылки – 100% не приветствуются, тут где-то правила форума были. Я, врать не буду, их не читал, но прения нескольколетней давности на этот счет помню.Marrenoloth
Exactamente писал(а): Тут не подскажу, 7.3 в глаза не видел Не думаю, что там радикально переделали. Ищите Events.Перечитал внимательнее. Не, про куда скрипты вставить – это я знаю, там всё по-старому. Пункт 2 – про сообщения. Сама программа управления сообщениями выглядит по-другому. В том числе вот этого окошка больше нет:
Вместо него сбоку панель со свойствами, в которых вот такой вкладки вообще нет.
А у меня просто вообще нет понимания как задаются какие-то динамические параметры в сообщениях.Exactamente
эм
первый раз это окно вижу) В 7.2 это выглядит вот так:
alarmlog.PNGВ 7.3 не знаю, ничего подсказать не могу. Возможно, они что-то сильно поменяли.
Marrenoloth
Всё. Разобрался. Чуть попозже выложу что и как, с копией исходной функции (пусть лучше в двух местах лежит, так спокойнее и надежнее). Типа мануала для нубов навроде меня 🙂Marrenoloth
Итак, саммари:
Взято отсюда: dfpd.siemens.ru/forum
Скрипт добавляется, как писал выше Exactamente, или копируется текст в новый Action script и аналогично компиляется.
Код функции:Код: Выделить всё
/* Автор: LexUS Добавляет сообщение о действии оператора DWORD dwFlags - FLAG_COMMENT_PARAMETER 0x00000001 - комментарий вводится напрямую при возникновении сообщения о действии оператора FLAG_COMMENT_DIALOG 0x00000003 - вызывается диалог добавления комментария при возникновении сообщения о действии оператора DWORD dwMsgNum - номер сообщения в Alarm Logging char* lpszPictureName - имя формы с которой производится ввод информации char* lpszObjectName - имя объекта в котором производится ввод информации char* lpszTagName - имя тэга char* lpszObjectName1 - наименование датчика, объекта char* lpszActionName - действие double doValueOld - предыдущее значение lpszTagName double doValueNew - новое значение lpszTagName char* pszComment - комментарий */ #pragma code ('kernel32.dll') void GetLocalTime( LPSYSTEMTIME lpSystemTime), BOOL GetComputerNameA(LPSTR Computername, LPDWORD size), #pragma code() #include 'apdefap.h' #define FLAG_COMMENT_PARAMETER 0x00000001 #define FLAG_COMMENT_DIALOG 0x00000003 //#define FLAG_TEXTID_PARAMETER 0x00000100 int n_GCreateMyOperationMsg( DWORD dwFlags, DWORD dwMsgNum, char* lpszPictureName, char* lpszObjectName, char* lpszTagName, char* lpszObjectName1, char* lpszActionName, float doValueOld, float doValueNew, char* pszComment ) { MSG_RTDATA_INSTANCECOMMENT_STRUCT MsgCreateEx, MSG_RTDATA_STRUCT MsgRTData, // for comment dialog CMN_ERROR scError, int iRet= FALSE, DWORD dwServiceID = 0, BOOL bOK, SYSTEMTIME time, DWORD dwBufSize = 256, char szComputerName[256] = '', char* szCurrentUser = NULL, char * pszServerPrefix = NULL, // char* pszPrefix, // to define the type of WinCC project // char* lpszParent, char szTmp[256] = '', // for diagnosis output printf('Start n_GCreateMyOperationMsg rn'), //====================================== // INIT_MESSAGE_STRUCT //====================================== memset(&MsgCreateEx,0,sizeof(MsgCreateEx)), memset(&MsgRTData,0,sizeof(MsgRTData)), memset(&scError,0,sizeof(scError)), GetLocalTime( MsgCreateEx.stMsgTime = time, MsgRTData.stMsgTime = time, MsgCreateEx.dwMsgNr = dwMsgNum, MsgRTData.dwMsgNr = dwMsgNum, MsgCreateEx.wPValueUsed = (WORD)(0x0000 ), // no real process value used MsgRTData.wPValueUsed = (WORD)(0x0000 ), MsgCreateEx.wTextValueUsed = 0x03FF, // text values 1 .. 10 used for textblocks 1 .. 10 MsgRTData.wTextValueUsed = 0x03FF, // text values 1 .. 10 used for textblocks 1 .. 10 MsgCreateEx.dwFlags = MSG_FLAG_TEXTVALUES, MsgRTData.dwFlags = MSG_FLAG_COMMENT | MSG_FLAG_TEXTVALUES, MsgCreateEx.dwMsgState = MSG_STATE_COME, MsgRTData.dwMsgState = MSG_STATE_COME, //====================================== // INITIALIZATION PROCESS VALUE BLOCKS 1..10 //====================================== GetComputerNameA(szComputerName, &dwBufSize), // sprintf(szTmp, 'Computername = %s rn', szComputerName), strncpy ( MsgCreateEx.mtTextValue[0].szText, szComputerName, sizeof (MsgCreateEx.mtTextValue[0].szText) - 1), // Computer Name // printf('Start n_GCreateMyOperationMsg rn'), szCurrentUser = GetTagChar('@local::@CurrentUser'), strncpy ( MsgCreateEx.mtTextValue[1].szText, szCurrentUser, sizeof (MsgCreateEx.mtTextValue[1].szText) - 1), // Current User Name MsgCreateEx.wPValueUsed = (WORD)(MsgCreateEx.wPValueUsed | 0x000C), MsgCreateEx.dPValue[2] = doValueOld, // old value MsgCreateEx.dPValue[3] = doValueNew, // new value strncpy ( MsgCreateEx.mtTextValue[4].szText, lpszPictureName, sizeof (MsgCreateEx.mtTextValue[4].szText) - 1), // lpszPictureName strncpy ( MsgCreateEx.mtTextValue[5].szText, lpszObjectName, sizeof (MsgCreateEx.mtTextValue[5].szText) - 1), // lpszObjectName strncpy ( MsgCreateEx.mtTextValue[6].szText, lpszTagName, sizeof (MsgCreateEx.mtTextValue[6].szText) - 1), // lpszTagName strncpy ( MsgCreateEx.mtTextValue[7].szText, lpszActionName, sizeof (MsgCreateEx.mtTextValue[7].szText) - 1), // lpszActionName strncpy ( MsgCreateEx.mtTextValue[8].szText, lpszObjectName1, sizeof (MsgCreateEx.mtTextValue[8].szText) - 1), // lpszObjectName1 //====================================== //====================================== // START_MESSAGE_SERVICE //====================================== memset(&scError,0,sizeof(scError)), // GetServerPrefix to determine MC or Server GetServerTagPrefix(&pszServerPrefix, NULL, NULL), //Return-Type: void if (NULL == pszServerPrefix) { printf('Serverapplication or Single Clientrn'), bOK = MSRTStartMsgService( &dwServiceID, NULL, NULL, 0, NULL, &scError ), // activate service } else { printf('MultiClient with Prefix : %srn',pszServerPrefix), //Return - Type :char* bOK = MSRTStartMsgServiceMC( &dwServiceID, NULL, NULL, 0, NULL,pszServerPrefix, &scError ), // activate service } if (bOK == FALSE) { printf('n_GCreateMyOperationMsg() - Unable to start message service! rn'), sprintf(szTmp, ' Error1 = 0x%0x, Errortext = %s rn', scError.dwError1, scError.szErrorText), printf(szTmp), return (-101), } //====================================== //====================================== // PARSE PARAMETERS //====================================== if ( ( dwFlags & FLAG_COMMENT_PARAMETER ) && ( NULL != pszComment ) ) { strncpy(MsgCreateEx.szComment, pszComment, sizeof (MsgCreateEx.szComment) - 1), MsgCreateEx.dwFlags |= MSG_FLAG_COMMENT, } if ( dwFlags & FLAG_COMMENT_DIALOG ) MsgCreateEx.dwFlags |= MSG_FLAG_COMMENT, //====================================== //====================================== // CREATE MESSAGE //====================================== bOK = MSRTCreateMsgInstanceWithComment(dwServiceID, &MsgCreateEx, &scError) , if ( TRUE == bOK) { if (FLAG_COMMENT_DIALOG == (dwFlags & FLAG_COMMENT_DIALOG) ) { BOOL bOkay, HWND hWnd = FindWindow(NULL, 'WinCC-Runtime - '), memset(&scError,0,sizeof(scError)), bOkay= MSRTDialogComment (hWnd, &MsgRTData, &scError), if (TRUE == bOkay) { MSG_COMMENT_STRUCT mComment, mComment.dwMsgNr = dwMsgNum, mComment.stTime = time, sprintf( mComment.szUser, MsgCreateEx.szUser, sizeof(mComment.szUser) - 1 ), memset(&scError,0,sizeof(scError)), bOkay = MSRTGetComment (dwServiceID, &mComment, &scError), if (TRUE == bOkay) { strncpy(MsgCreateEx.szComment, mComment.szText, sizeof (MsgCreateEx.szComment) - 1), } } else { printf('#E201: n_GCreateMyOperationMsg() - Error at MSRTGetComment() szErrorText='%s' error2=%drn', scError.szErrorText, scError.dwError2), iRet = -201, } } } if(bOK == FALSE) { printf ('#E301: n_GCreateMyOperationMsg() - Error at MSRTCreateMsgInstanceWithComment() szErrorText='%s'rn', scError.szErrorText), iRet = -301, } //====================================== //====================================== // STOP_MESSAGE_SERVICE //====================================== bOK= MSRTStopMsgService( dwServiceID, &scError), printf('End n_GCreateMyOperationMsg rn'), return (iRet), }
Пример на OnClick объекта:
Код: Выделить всё
#include 'apdefap.h' void OnLButtonDown(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName, UINT nFlags, int x, int y) { // WINCC:TAGNAME_SECTION_START // syntax: #define TagNameInAction 'DMTagName' // next TagID : 1 #define TAG_1 'Main/Total/allPlatesDel.Clr_L1' // WINCC:TAGNAME_SECTION_END // WINCC:PICNAME_SECTION_START // syntax: #define PicNameInAction 'PictureName' // next PicID : 1 // WINCC:PICNAME_SECTION_END HWND Handle=NULL, Handle=FindWindow('PDLRTisAliveAndWaitsForYou','WinCC-Runtime - '), if (MessageBox(Handle,' Очистить все листы? ','Очистка всех листов из ССМ',MB_YESNO|MB_ICONWARNING|MB_SYSTEMMODAL|MB_SETFOREGROUND)==6){ // n_GCreateMyOperationMsg( DWORD dwFlags, DWORD dwMsgNum, char* lpszPictureName, char* lpszObjectName, char* lpszTagName, char* lpszObjectName1, char* lpszActionName, float doValueOld, float doValueNew, char* pszComment ) n_GCreateMyOperationMsg( 0x00000001, 1009, lpszPictureName, lpszObjectName, TAG_1, GetPropChar(lpszPictureName, lpszObjectName, 'ToolTipText'), '', 0, 1, ''), // MessageBox(Handle,' Готово ','Лист удален',MB_OK|MB_ICONINFORMATION|MB_SYSTEMMODAL|MB_SETFOREGROUND), SetTagBit(TAG_1, 1), } }
Аналогично на событие Input Value текстового поля (только автор там еще модифицированную функцию указал – править нужно, но идея понятна:
Код: Выделить всё
#include 'apdefap.h' void OnPropertyChanged(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName, char* value) { // WINCC:TAGNAME_SECTION_START // syntax: #define TagNameInAction 'DMTagName' #define v_str_length 4 // next TagID : 1 // WINCC:TAGNAME_SECTION_END // WINCC:PICNAME_SECTION_START // syntax: #define PicNameInAction 'PictureName' // next PicID : 1 // WINCC:PICNAME_SECTION_END LINKINFO pLink, char v_str[v_str_length+1] = '', if (PDLRTGetLink (0, lpszPictureName, lpszObjectName, 'OutputValue', &pLink, NULL, NULL, NULL)) { strncpy(v_str, value, v_str_length), SetTagChar(pLink.szLinkName, v_str), //Return-Type: BOOL n_GCreateMyOperationMsg_str( 0x00000001, 1004, lpszPictureName, lpszObjectName, pLink.szLinkName, GetPropChar(lpszPictureName, lpszObjectName, 'ToolTipText'), '',GetPropChar(lpszPictureName, lpszObjectName, 'OutputValue'), GetPropChar(lpszPictureName, lpszObjectName, 'InputValue'), ''), } }
Текстовые сообщения формируются, с @1%s@ по @9%s@, где формируется 1%s по правилам языка С. (Внимание, в данной функции 3 и 4 параметры – с литерой d, а не s, т.к. числа). Текст сообщения для теста и понимания что кладется в какую переменную:
Код: Выделить всё
@1%s@, @2%s@, @3%d@, @4%d@, @5%s@, @6%s@, @7%s@, @8%s@, @9%s@
Большая часть параметров – обычные тексты, но в примерах к событиям автор показал как туда подставить различные параметры объектов.
Marrenoloth
Вообще, как я понимаю, мануал больше для нубов, ибо скрипт берет сообщение, внешние тексты и пихает одно в другое. Но для тех, кто не знает С – это магия какая-то. 🙁Felix
Marrenoloth писал(а): Вообще, как я понимаю, мануал больше для нубов, ибо скрипт берет сообщение, внешние тексты и пихает одно в другое. Но для тех, кто не знает С – это магия какая-то. 🙁В данный момент для меня это магия .
LexSL
Я тоже какое-то время ‘заморачивался’ на C с этой функцией, но потом во всех последующих проектах делал на VBS:
например, при нажатии кнопки вызывается вот такая функция:Код: Выделить всё
Function CreateAlarm(AlarmId, text) Const hmiAlarmStateCome = 1 Dim user, Alarm user = HMIRuntime.Tags('@CurrentUser').Read Set Alarm = HMIRuntime.Alarms(AlarmId) Alarm.ProcessValues(5).Value = '' & text Alarm.ProcessValues(4).Value = '' & user Alarm.State = hmiAlarmStateCome Alarm.UserName = user Alarm.Create End Function
AlarmId – номер сконфигурированного в AlarmLogging сообщения, text – нужный текст сообщения (например, ‘Подана команда ‘ОТКРЫТЬ”)
AlarmLogging.png
-
- Вы должны войти в систему, чтобы ответить в этой теме.