El objetivo es desde la sala de control poder escuchar el mensaje de ciertas alarmas catalogadas como críticas
Por defecto WinCC no tiene esta funcionalidad, y es lo que vamos a agregar, vamos a crear una librería en C++ para darle esta funcionalidad.
El código de nuestra función es el siguiente, y si quieres crear tu propia librería aquí escribi un artículo hace ya unos años 😉
#include <sapi.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <codecvt>
extern "C"
{
__declspec(dllexport) void SpeakText(char* input);
}
__declspec(dllexport) void SpeakText(char* input)
{
// Initialize COM
HRESULT hr = ::CoInitialize(NULL);
if (FAILED(hr)) return;
ISpVoice* pVoice = NULL;
// Create voice instance
hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&pVoice);
if (SUCCEEDED(hr)) {
// Convert char* to std::wstring
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
std::wstring wtext = converter.from_bytes(input);
// Speak the text
pVoice->Speak(wtext.c_str(), SPF_IS_XML, NULL);
pVoice->Release();
}
::CoUninitialize();
}
Una vez que ya tenemos nuestra librería compilada, vamos a adaptar la función GMsgFunct con el siguiente código
BOOL GMsgFunction( char* pszMsgData)
{
#pragma code("C:/Controls/PHSSpeechSynth.dll")
void SpeakText(char* input);
#pragma code()
BOOL bOK;
WORD wTextBlock = 0;
DWORD dwTextNr = 34 ;
MSG_TEXT_STRUCT scMsgText;
CMN_ERROR scError;
MSG_CSDATA_STRUCT sM;
CMN_ERROR pError;
MSG_RTDATA_STRUCT mRT;
memset( &mRT, 0, sizeof( MSG_RTDATA_STRUCT ) );
if( pszMsgData != NULL )
{
printf( "Meldung : %s \r\n", pszMsgData );
// Meldungsdaten einlesen
sscanf( pszMsgData, "%ld,%ld,%04d.%02d.%02d,%02d:%02d:%02d:%03d,%ld, %ld, %ld, %d,%d",
&mRT.dwMsgNr, // Meldungsnummer
&mRT.dwMsgState, // Status MSG_STATE_COME, .._GO, .._QUIT, .._QUIT_SYSTEM
&mRT.stMsgTime.wYear, // Jahr
&mRT.stMsgTime.wMonth, // Monat
&mRT.stMsgTime.wDay, // Tag
&mRT.stMsgTime.wHour, // Stunde
&mRT.stMsgTime.wMinute, // Minute
&mRT.stMsgTime.wSecond, // Sekunde
&mRT.stMsgTime.wMilliseconds, // Millisekunde
&mRT.dwTimeDiff, // Zeitdauer der anstehenden Meldung
&mRT.dwCounter, // Interner Meldungszähler
&mRT.dwFlags, // Flags( intern )
&mRT.wPValueUsed,
&mRT.wTextValueUsed );
// Prozesswerte lesen, falls gewünscht
}
printf("Nr : %d, St: %x, %d-%d-%d %d:%d:%d.%d, Dur: %d, Cnt %d, Fl %d\r\n" ,
mRT.dwMsgNr, mRT.dwMsgState, mRT.stMsgTime.wDay, mRT.stMsgTime.wMonth, mRT.stMsgTime.wYear,
mRT.stMsgTime.wHour, mRT.stMsgTime.wMinute, mRT.stMsgTime.wSecond, mRT.stMsgTime.wMilliseconds, mRT.dwTimeDiff,
mRT.dwCounter, mRT.dwFlags ) ;
// Add this function to get information
MSRTGetMsgCSData(mRT.dwMsgNr, &sM, &pError);
printf ("MSRTGetMsgCSData() - szText=\"%i\"\r\n", sM.dwTextID[0]);
// Retrieve the message text
bOK = MSRTGetMsgText(wTextBlock, sM.dwTextID[0], &scMsgText, &scError );
if (bOK)
{
printf ("MSRTGetMsgText() - szText=\"%s\"\r\n", scMsgText.szText);
SpeakText(scMsgText.szText);
}
return( TRUE );
}
La parte mas importante es la declaración de la función de nuestra librería


El siguiente paso es habilitar Triggers action, Loop in Alarm a las alarmas que nos interesan con la función que hayamos definido

Y ahora activa los altavoces para comprobar su funcionamiento… 😉
Dedicado a mi buen compañero Rafael Hernández Martínez por las lecciones sobre DCS particularmente DeltaV.
Cuando trabajas en equipo y compartes, lo único que puede pasar es que te beneficies 😉