The objective is to be able to hear the message of certain alarms classified as critical from the control room
By default WinCC does not have this functionality, and that is what we are going to add, we are going to create a library in C++ to provide this functionality.
The code of our function is as follows, and if you want to create your own library, here I wrote an article a few years ago ;-)
#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();
}
Once we have our library compiled, we will adapt the GMsgFunct function with the following code
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( "Message : %s
", pszMsgData );
// Read message data
sscanf( pszMsgData, "%ld,%ld,%04d.%02d.%02d,%02d:%02d:%02d:%03d,%ld, %ld, %ld, %d,%d",
&mRT.dwMsgNr, // Message number
&mRT.dwMsgState, // Status MSG_STATE_COME, .._GO, .._QUIT, .._QUIT_SYSTEM
&mRT.stMsgTime.wYear, // Year
&mRT.stMsgTime.wMonth, // Month
&mRT.stMsgTime.wDay, // Day
&mRT.stMsgTime.wHour, // Hour
&mRT.stMsgTime.wMinute, // Minute
&mRT.stMsgTime.wSecond, // Second
&mRT.stMsgTime.wMilliseconds, // Millisecond
&mRT.dwTimeDiff, // Duration of the pending message
&mRT.dwCounter, // Internal message counter
&mRT.dwFlags, // Flags (internal)
&mRT.wPValueUsed,
&mRT.wTextValueUsed );
// Read process values, if desired
}
printf("Nr : %d, St: %x, %d-%d-%d %d:%d:%d.%d, Dur: %d, Cnt %d, Fl %d
" ,
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\"
", sM.dwTextID[0]);
// Retrieve the message text
bOK = MSRTGetMsgText(wTextBlock, sM.dwTextID[0], &scMsgText, &scError );
if (bOK)
{
printf ("MSRTGetMsgText() - szText=\"%s\"
", scMsgText.szText);
SpeakText(scMsgText.szText);
}
return( TRUE );
}
The most important part is the declaration of the function from our library
The next step is to enable Triggers action, Loop in Alarm for the alarms that interest us with the function we have defined
And now activate the speakers to check their operation... ;-)
Dedicated to my good colleague Rafael Hernández Martínez for the lessons on DCS particularly DeltaV.
When you work as a team and share, the only thing that can happen is that you benefit ;-)