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 😉

Descargar la librería