SIEMENS, DF&PD

Предыдущее посещение: Ср июл 06, 2016 5:05 Текущее время: Ср июл 06, 2016 5:05

Часовой пояс: UTC + 3 часа




 [ Сообщений: 13 ] 
Автор Сообщение
 Заголовок сообщения: Проблема с синхронизацияей сообщений резервируемых серверов.
СообщениеДобавлено: Пт май 12, 2006 11:01 
Не в сети
Новый писатель

Зарегистрирован: Чт мар 16, 2006 13:50
Сообщения: 34
Доброго времени суток.
Есть два скада сервера на них крутятся WinCC6.0SP2. Проблема заключается в следующем.. При поступлении сообщения с контроллера, если его квитировать с клиента, который висит на основном SCADA1 сервере (или квитировать на самом SCADA1), то на SCADA2- резервном это сообщение висит в WnCCAlarmControl неквитированным.
Возникла мысль написать два сокета и крутить их соотвественно на SCADA1 и SCADA2 в фоновом режиме. И посредством их передавать состояние сообщения (квитировано или нет).Итак мое понимание решения данной задачи сводится к написанию двух макросов на SCADE1 (получать некоторые уникальные идентификаторы квитированных сообщений по событию "нажатие кнопки квитирования" и передавать в буфер сокета для передачи на SCADA2) и на SCADA2(чтение из буфера информации и квитирование сообщения по переданным уникальным идентификаторам(которые насколько я понимаю должны быть одинаковыми и на первом и на втором SCADA серверах)).
И теперь извечный вопрос КАК, какие уникальные идентификаторы у сообщений, какие существуют функции для их получения и изменения состояния "квитировано- не квитировано" ???? Полазил в ODK в файле Alarm Loging нашёл структуру MSG_CSDATA_STRUCT(описывает она сообщение??) с описанием её полей
одно из этих полей MSgNr (номер сообщения??)нашёл функции, которые, как мне показалось, подходят для решения задачи на SCADA2: MSRTGetMsgCSData и AknowledgeMessage(MsgNr).
А вот для задачи на SCADA1 ,к сожалению, ничего подходящего не увидел..:(
Буду весьма признателен всем, кто меня поправит или расскажет как решить данную проблему.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Пт май 12, 2006 14:11 
Не в сети
Новый писатель

Зарегистрирован: Чт мар 16, 2006 13:50
Сообщения: 34
Нашёл ещё одну структуру MSG_RTDATA_STRUCT:

typedef struct {

DWORD dwMsgState;

DWORD dwMsgNr;

SYSTEMTIME stMsgTime;

DWORD dwTimeDiff;

DWORD dwCounter;

DWORD dwFlags;

WORD wPValueUsed;

WORD wTextValueUsed;

double dPValue[MSG_MAX_PVALUE];

MSG_TEXTVAL_STRUCT mtTextValue[MSG_MAX_PVALUE];

TCHAR szInstance[MSG_MAX_INSTANCE+1];

}

MSG_RTDATA_INSTANCE_STRUCT;

по полям очень уж похоже на то, что определяет сообщение.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Пт май 12, 2006 23:44 
Не в сети
Известный Писатель

Зарегистрирован: Сб янв 15, 2005 0:49
Сообщения: 466
Откуда: Уфа
Преамбула
WinCC при появлении оперативного сообщения в системе (аларма) записывает его в БД MS SQL Server'а. Посмотрим какие БД по сообщениям имеет SQL Server. Открываем Пуск->Программы->Microsoft SQL Server->Enterprise Manager (ЕМ).
В EM открываем Console Root->Microsoft SQL Servers->SQL Server Group->Имя компьютера\WINCC->Databases. Все БД, которые имеют в своем названии слово "ALG", содержат эти сообщения. Название БД формируется по принципу Имя компьютера_Имя проекта_ALG_Год Месяц Число Час Минута_Год Месяц Число Час Минута.

Чтобы найти последнее оперативное сообщение (аларм) в системе нужно выбрать последнюю созданную БД с сообщениями. Открываем ее. Выбираем Tables. В таблице AlgCSDataRUS хранятся все доступные сообщения системы. В таблице MsArcLong хранится архив сообщений. Для открытия таблицы выделяем ее, затем пр.кн. мыши, выбираем Open Table -> Return All Rows.
Значение полей в таблице MsArcLong:
1) MsgNr - номер сообщения в системе (AlarmLogging) (по этому номеру можно вытащить информацию - текст, параметры сообщения из таблицы AlgCSDataRUS)
2) DateTime - дата/время появления сообщения в системе
3) State - состояние сообщения (1 - пришедшее, 2 - ушедшее, 3 - квитированное пользователем, 4 -заблокированное, 5 - разблокированное, 16 - квитированное системой и т.д.)

Амбула
На основе вышеизложенного можно предложить следующее. На сервере SCADA1 создаем кнопку "Квитировать". В обработчике этой кнопке реализуем следующий алгоритм:
1) квитируем выделенное сообщение функцией AXC_OnBtnSinglAckn(char* lpszPictureName, char* pszMsgWin).
2) узнаем номер последнего квитированного сообщения - это самописная функция GetMessageNumber (см.ниже). Есть еще в ODK функция MSRTGetSelectedMsg, которая возвращает инфу выделенного в AlarmControl сообщения.
3) передаем этот номер на сервер SCADA2.
На сервере SCADA2 при получении номера последнего квитированного сообщения возникает событие, в котором при помощи функции AcknowledgeMessage(DWORD MsgNr) квитируем это сообщение.
На сервере SCADA2 создаем такую же кнопку "Квитировать".

Алгоритм функции GetMessageNumber:
1) получаем название последней БД с оперативными сообщениями
SELECT TOP 1 [name] FROM [SCADA1\WINCC].[master].[dbo].[sysdatabases] WHERE [name] LIKE '%ALG%' ORDER BY [crdate] DESC,
где SCADA1 - название компьютера-сервера №1.
2) из этой БД из таблицы MsArcLong получаем номер последнего квитированного сообщения
SELECT TOP 1 [MsgNr] FROM [SCADA1\WINCC].[Название последней БД].[dbo].[MsArcLong]
WHERE [State]=3 ORDER BY [DateTime] DESC

Постамбула
Изменить статус сообщения на сервере SCADA2 можно сначала в БД сервера SCADA2 при помощи оператора UPDATE (все это можно выполнить с сервера SCADA1 из обработчика кнопки "Квитировать"), а потом сделать что-то типа Refresh (обновить) для Alarm Control'а...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Пн май 15, 2006 15:15 
Не в сети
Новый писатель

Зарегистрирован: Чт мар 16, 2006 13:50
Сообщения: 34
Спасибо огромное уважаемый Hied.
Решил воспользоваться для считывания номера сообщения функцией
MSRTGetSelectedMsg (LPCTSTR lpszTemplate,
LPMSG_RTDATA_STRUCT lpMsgRT,
LPCMN_ERROR lpError)
, только непонятно что такое lpszTemplate. В ODK написано это указатель на имя окна или кэпшена controla, в котором сообщение выделено. Записываю туда "00_Событие.pdl". Вот листинг макроса , привязанного к кнопке квитирования.

#include "apdefap.h"
void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)
{
CMN_ERROR Err,Error;
TCHAR szTemplate[255];
MSG_RTDATA_STRUCT MsgRT;

int i;

//Запись имени пользователя, машины и значения сообщения
SetTagChar("ASUE_SERVER_SCADA1::Ack_User",GetTagChar("@local::@CurrentUser"));
SetTagChar("ASUE_SERVER_SCADA1::Ack_Machine",GetTagChar("@local::@LocalMachineName"));
SetTagChar("ASUE_SERVER_SCADA1::Ack_Value","Квитировано");

//Задержка времени
for (i=0; i<=5000000; i=i+1)
{};

//Квитирование
if (!(AXC_OnBtnSinglAckn(lpszPictureName,"Control1")))
//if (!(AXC_OnBtnVisibleAckn(lpszPictureName,"Control1")))
{
printf ("Keine Selektion des ALG-Controls\r\n");
}

switch(GetTagWord("ASUE_SERVER_SCADA1::Ack_Trigger"))
{
case 0:
SetTagWord("ASUE_SERVER_SCADA1::Ack_Trigger",1);
break;

case 1:
SetTagWord("ASUE_SERVER_SCADA1::Ack_Trigger",0);
break;

}
//Извлечение номера сообщения MsgNr
strcpy(szTemplate, "000_События.PDL");
memset(&MsgRT,0,sizeof(MSG_RTDATA_STRUCT));//заполнение нулями структуры MsgRT
MSRTGetSelectedMsg(szTemplate,&MsgRT,&Error );
SetTagDWord("Msg_Nr_Send.MsgNr",MsgRT.dwMsgNr);
SetTagBit("Msg_Nr_Send.AcknAll",0);
SetTagBit("Msg_Nr_Send.AcknSingle",1);
}
где Msg_Nr_Send структурированный тег, который содержит в себе поля-тэги dwMsgNr (тип DWORD -номер сообщения),AcknSingle (тип BIT- в случае если нажата эта кнопка, то этот флажок устанавливается в 1 сигнализируя тем самым изменение тега и указывает что SCADе2 нужно квитировать только одно данное сообщение); AcknAll (тип BIT- устанавливаясь в 1 сигнализирует SCADе2 о том, что надо запустить скрипт который квитирует все сообщения зависшие в controle).
Запускаю WInCC генерирую сообщение, квитирую его данной кнопкой, смотрю состояние тегов Msg_Nr_Send.MsgNr, Msg_Nr_Send.AcknAll, Msg_Nr_Send.AcknSingle...а показывают они всё равно заданные по умолчанию на загрузку значения =0..:(


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Пн май 15, 2006 17:36 
Не в сети
Известный Писатель

Зарегистрирован: Сб янв 15, 2005 0:49
Сообщения: 466
Откуда: Уфа
Попробуй подставить вместо имени окна имя контрола
Код:
strcpy(szTemplate, "Control1");

Отладь каждую строчку своего кода (при помощи printf) - проверь каждое значение тега и структуры, какая строка не выполняется, где появляется ошибка.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Вт май 16, 2006 10:33 
Не в сети
Новый писатель

Зарегистрирован: Чт мар 16, 2006 13:50
Сообщения: 34
Попробовал "Control1"...не работает :( Вообщем возвращает 0 и всё...
Вообщем Control1 находится на 000_События.pdl, может как то более полно надо написать вроде типа "000_События.pdl_Control1"? :-/


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Вт май 16, 2006 12:58 
Не в сети
Известный Писатель

Зарегистрирован: Сб янв 15, 2005 0:49
Сообщения: 466
Откуда: Уфа
Цитата:
Попробовал "Control1"...не работает :( Вообщем возвращает 0 и всё... Вообщем Control1 находится на 000_События.pdl, может как то более полно надо написать вроде типа "000_События.pdl_Control1"? :-/

Нет, оставь strcpy(szTemplate, "Control1");

Отладь свой код при помощи printf, если в теги не пишутся значения, то, наверное, твой код не доходит до записи значений в теги - где-то возникает ошибка. Помести на форму GSC Diagnostic и поставь printf и посмотри где printf не выполняется...
Код:
#include "apdefap.h"
void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)
{
CMN_ERROR Err,Error;
TCHAR szTemplate[255];
MSG_RTDATA_STRUCT MsgRT;

int i;

//Запись имени пользователя, машины и значения сообщения
SetTagChar("ASUE_SERVER_SCADA1::Ack_User",GetTagChar("@local::@CurrentUser"));
SetTagChar("ASUE_SERVER_SCADA1::Ack_Machine",GetTagChar("@local::@LocalMachineName"));
SetTagChar("ASUE_SERVER_SCADA1::Ack_Value","Квитировано");

printf("OK1\r\n");

//Задержка времени
for (i=0; i<=5000000; i=i+1)
{};

//Квитирование
if (!(AXC_OnBtnSinglAckn(lpszPictureName,"Control1")))
//if (!(AXC_OnBtnVisibleAckn(lpszPictureName,"Control1")))
{
printf ("Keine Selektion des ALG-Controls\r\n");
}

printf("OK2\r\n");

switch(GetTagWord("ASUE_SERVER_SCADA1::Ack_Trigger"))
{
case 0:
SetTagWord("ASUE_SERVER_SCADA1::Ack_Trigger",1);
break;

case 1:
SetTagWord("ASUE_SERVER_SCADA1::Ack_Trigger",0);
break;

}

printf("OK3\r\n");

//Извлечение номера сообщения MsgNr
strcpy(szTemplate, "Control1");
memset(&MsgRT,0,sizeof(MSG_RTDATA_STRUCT));//заполнение нулями структуры MsgRT
MSRTGetSelectedMsg(szTemplate,&MsgRT,&Error );

printf("OK4\r\n");

SetTagDWord("Msg_Nr_Send.MsgNr",MsgRT.dwMsgNr);
SetTagBit("Msg_Nr_Send.AcknAll",0);
SetTagBit("Msg_Nr_Send.AcknSingle",1);

printf("OK5\r\n");
}


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Вт май 16, 2006 15:51 
Не в сети
Новый писатель

Зарегистрирован: Чт мар 16, 2006 13:50
Сообщения: 34
Так, вышеизложенный хелп ещё не пробовал... На текущий момент с текущим кодом:

Код:
#include "apdefap.h"
void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)
{
CMN_ERROR Err,Error;
TCHAR szTemplate[255];
MSG_RTDATA_STRUCT MsgRT;
BOOL ret=FALSE;
int i;

//Запись имени пользователя, машины и значения сообщения
SetTagChar("ASUE_SERVER_SCADA1::Ack_User",GetTagChar("@local::@CurrentUser"));
SetTagChar("ASUE_SERVER_SCADA1::Ack_Machine",GetTagChar("@local::@LocalMachineName"));
SetTagChar("ASUE_SERVER_SCADA1::Ack_Value","Квитировано");

//Задержка времени
for (i=0; i<=5000000; i=i+1)
{};

//Квитирование
if (!(AXC_OnBtnSinglAckn(lpszPictureName,"Control1")))
//if (!(AXC_OnBtnVisibleAckn(lpszPictureName,"Control1")))
{
  printf ("Keine Selektion des ALG-Controls\r\n");
}

switch(GetTagWord("ASUE_SERVER_SCADA1::Ack_Trigger"))
{
case 0:
SetTagWord("ASUE_SERVER_SCADA1::Ack_Trigger",1);
break;

case 1:
SetTagWord("ASUE_SERVER_SCADA1::Ack_Trigger",0);
break;
}
printf("%s \n",lpszPictureName );
printf("%s \n",lpszObjectName );

//Извлечение номера сообщения  MsgNr
strcpy(szTemplate, "Control1");
memset(&MsgRT,0,sizeof(MSG_RTDATA_STRUCT));//заполнение нулями структуры MsgRT
ret =MSRTGetSelectedMsg(szTemplate,&MsgRT,&Error );
if(FALSE == ret)

    {

        printf("Error in MSRTGetMsgCSData: E1= 0x%08lx ; E2= 0x%08lx ; %s",

            Error.dwError1, Error.dwError2, Error.szErrorText);

    }
SetTagDWord("Msg_Nr_Send.MsgNr",MsgRT.dwMsgNr);
SetTagBit("Msg_Nr_Send.AcknAll",0);   
SetTagBit("Msg_Nr_Send.AcknSingle",1);
   
}

С помощью apdiag.exe ыведены следующие сообщения

000_START.ОКНОКАДА2:000_СОБЫТИЯ

Button34

Error in MSRTGetMsgCSData: E1= 0x00000004 ; E2= 0x00000000 ; WinCC Runtime: PDL RT: Объект не найден


Как я и предполагал, кажется не может найти Control1
Что же делать, что же запихать туда в MSRTGetSelectedMsg, чтобы она заработала...:(


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Вт май 16, 2006 21:39 
Не в сети
Известный Писатель

Зарегистрирован: Сб янв 15, 2005 0:49
Сообщения: 466
Откуда: Уфа
Все очень просто, сел, проверил, работает. В ОДК же сказано
Цитата:
Parameters

lpszTemplate

Pointer to the name of the window or the caption of the control in which a message is selected.


У объекта с названием Control1 есть свойство Caption. Вот в это свойство пишем тоже Control1 и все...

strcpy(szTemplate, "Control1"); - а в этот параметр записывается "...caption of the control..."


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Ср май 17, 2006 7:40 
Не в сети
Новый писатель

Зарегистрирован: Чт мар 16, 2006 13:50
Сообщения: 34
Вот в это свойство пишем тоже Control1 и все...

strcpy(szTemplate, "Control1"); - а в этот параметр записывается "...caption of the control..."


У меня caption был вообще не задан, задал его на свойстве Control1
и присвоил имя AlarmEvent
Извинияюсь за свою тупость, непонятно, то есть как записываем
strcpy(szTemplate, "Control1.AlarmEvent");?????????
Уважаемый Hied,пожалуйста можно поподробнее?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Ср май 17, 2006 10:19 
Не в сети
Известный Писатель

Зарегистрирован: Сб янв 15, 2005 0:49
Сообщения: 466
Откуда: Уфа
Если Caption = AlarmEvent, то пишем strcpy(szTemplate, "AlarmEvent");


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Ср май 17, 2006 15:13 
Не в сети
Новый писатель

Зарегистрирован: Чт мар 16, 2006 13:50
Сообщения: 34
ОГРОМНЕЙШЕЕ СПАСИБО!!!!
Функция возвращает номер! :-)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Чт май 18, 2006 15:05 
Не в сети
Новый писатель

Зарегистрирован: Чт мар 16, 2006 13:50
Сообщения: 34
Только вот теперь возникла другая трудность.... вообщем, как и наметил себе, создал структурный тип MsgNrSend c полями , создал в разделе "внутренние теги " тег типа Msg_Nr_Send с полями DWORD MsgN, BIT AcknAll , BIT AcknSingle. Импортировал этот тег по OPC каналу но при изменении этого тега на SCADA1, на SCADA2 он всё равно в начальных значениях. Синхронизация не происходит...
Как передавать структурный тип по OPC?
Или может есть другая возможность в WinCC передать на все машины структурный тег.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
 [ Сообщений: 13 ] 

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: Google [Bot] и гости: 5


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения

Перейти:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group