- FunctionCore:
- Implement UnlinkChannel()
- Avoid memory errors on application shutdown (state events)
- Unlink channels before destroying in Function destructor
- Bug fix: Linked Channels not added correctly
447 lines
15 KiB
C++
447 lines
15 KiB
C++
/*
|
|
* FunctionCore.cpp
|
|
*
|
|
* Created on: 18 May 2016
|
|
* Author: wentzelc
|
|
*/
|
|
|
|
// Standard C/C++ Libraries
|
|
/* none */
|
|
|
|
// redA Libraries
|
|
#include "ApplicationCore.h"
|
|
#include "FunctionCore.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Global Vars
|
|
extern char * ProcessName;
|
|
extern CApplication * Application;
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Function Constructor
|
|
//CFunctionCore * NewFunctionCore( const char * Name ) {
|
|
// return new CFunctionCore( Name, "Function" );
|
|
//}
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Life cycle
|
|
CFunctionCore::CFunctionCore( const char * pName, const char * pType ) : Type( pType )
|
|
{
|
|
// Set name
|
|
if (pName)
|
|
Name = strdup( pName );
|
|
|
|
// Logging
|
|
Log = Application->Log;
|
|
LogLevel = dlNone;
|
|
LogOutput = loNormal;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CFunctionCore::~CFunctionCore()
|
|
{
|
|
TChannel * NextChannel = NULL;
|
|
TChannelLink * NextLinkedChannel = NULL;
|
|
|
|
// Destroy Channels
|
|
while (FirstChannel)
|
|
{
|
|
// Destroy Linked Channels
|
|
while (FirstChannel->FirstLink)
|
|
UnlinkChannel( FirstChannel->Name, FirstChannel->FirstLink->Function->Name, FirstChannel->FirstLink->Channel->Name );
|
|
|
|
// Destroy Parameters
|
|
if (FirstChannel->Name)
|
|
free( FirstChannel->Name );
|
|
if (FirstChannel->Ref)
|
|
free( FirstChannel->Ref );
|
|
|
|
// Destroy Channel
|
|
NextChannel = FirstChannel->Next;
|
|
delete FirstChannel;
|
|
FirstChannel = NextChannel;
|
|
}
|
|
|
|
// Report status
|
|
if (Log) Log->Message( LogLevel, dlLow, "%s: Function '%s' - Destroyed", ProcessName, Name );
|
|
|
|
// Destroy Name
|
|
if (Name) free( Name );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CFunctionCore::Init( CDataMember * FunctionConfig )
|
|
{
|
|
// Load configuration
|
|
CDataMember * LogConfig;
|
|
CDataMember * ChannelConfig;
|
|
EDebugLevel pLogLevel;
|
|
int pLogOutput;
|
|
char * ConfigName = NULL;
|
|
|
|
// Validate
|
|
if (!FunctionConfig )
|
|
return false;
|
|
|
|
// Set Type
|
|
FunctionConfig->SetChStr( "Type", Type );
|
|
|
|
// Configure Logging/Debugging
|
|
LogConfig = FunctionConfig->GetChild( "Log", true );
|
|
pLogLevel = Log->ReadLogLevel( LogConfig );
|
|
pLogOutput = Log->ReadLogOutput( LogConfig );
|
|
SetLogParam( pLogLevel, pLogOutput );
|
|
|
|
// Load Channels
|
|
ChannelConfig = FunctionConfig->GetChFirstChild( "Channels", true );
|
|
while (ChannelConfig) {
|
|
AddChannel( ChannelConfig->GetName(), CH_off );
|
|
ChannelConfig = ChannelConfig->GetNextPeer();
|
|
}
|
|
|
|
// Get main configuration
|
|
if ((Config = FunctionConfig->GetChild( "Config", true )) && Config->isString()) {
|
|
ConfigName = (char*)FunctionConfig->GetChStr( "Config" );
|
|
Config = Application->Config->GetChild( ConfigName, true );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CFunctionCore::InitChannelLinks( CDataMember * LinkConfig )
|
|
{
|
|
TChannel * Channel;
|
|
CDataMember * ChannelMember;
|
|
CDataMember * FunctionMember;
|
|
|
|
// Validate
|
|
if (!LinkConfig)
|
|
return false;
|
|
|
|
// Process each Channel
|
|
ChannelMember = LinkConfig->GetFirstChild();
|
|
while (ChannelMember)
|
|
{
|
|
if ((Channel = GetChannel( ChannelMember->GetName() )))
|
|
{
|
|
FunctionMember = ChannelMember->GetElement( 0 );
|
|
while (FunctionMember)
|
|
{
|
|
// Get Parameters
|
|
LinkChannel( Channel->Name,
|
|
FunctionMember->GetChStr( "Function", NULL, true ),
|
|
FunctionMember->GetChStr( "Channel", NULL, true ),
|
|
FunctionMember->GetChBool( "Input", false, true ),
|
|
FunctionMember->GetChBool( "Output", false, true ) );
|
|
FunctionMember = FunctionMember->GetNextPeer();
|
|
}
|
|
}
|
|
ChannelMember = ChannelMember->GetNextPeer();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CFunctionCore::SetLogParam( EDebugLevel pDebugLevel, int pOutputDisplay )
|
|
{
|
|
// Output
|
|
LogLevel = pDebugLevel;
|
|
LogOutput = pOutputDisplay;
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CFunctionCore::SetLogLevel( EDebugLevel pDebugLevel )
|
|
{
|
|
LogLevel = pDebugLevel;
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
TChannel * CFunctionCore::AddChannel( const char * ChannelName, const EChannelState State )
|
|
{
|
|
TChannel ** Channel = NULL;
|
|
|
|
// Validate
|
|
if (!ChannelName) {
|
|
return NULL;
|
|
}
|
|
|
|
// Check if exists
|
|
Channel = &FirstChannel;
|
|
while (*Channel && strcmp( ChannelName, (*Channel)->Name )) {
|
|
Channel = &((*Channel)->Next);
|
|
}
|
|
|
|
// Create if not exist
|
|
if (!*Channel) {
|
|
// Create
|
|
*Channel = new TChannel;
|
|
|
|
// Set Name
|
|
(*Channel)->Name = strdup( ChannelName );
|
|
(*Channel)->Ref = (char*)malloc( strlen(Name)+strlen(ChannelName)+2 );
|
|
sprintf( (*Channel)->Ref, "%s/%s", Name, ChannelName );
|
|
(*Channel)->State = State;
|
|
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Channel '%s' - Created, Ref:'%s', State:%s",
|
|
ProcessName, Name, ChannelName, (*Channel)->Ref, ChannelStateName[(*Channel)->State] );
|
|
}
|
|
return *Channel;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CFunctionCore::SetChannelState( TChannel * Channel, const EChannelState State )
|
|
{
|
|
EChannelState OldState = Channel->State;
|
|
TChannelLink * LinkChannel;
|
|
|
|
// Validate
|
|
if (!Channel)
|
|
return false;
|
|
|
|
// Update state
|
|
Channel->State = State;
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Update Channel '%s' - State:%s",
|
|
ProcessName, Name, Channel->Name, ChannelStateName[State] );
|
|
|
|
// Update linked channels
|
|
LinkChannel = Channel->FirstLink;
|
|
while (LinkChannel) {
|
|
LinkChannel->Function->ChannelStateEvent( LinkChannel->Channel, Channel->Ref, OldState, State );
|
|
LinkChannel = LinkChannel->Next;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CFunctionCore::ChannelStateEvent( TChannel * Channel, const char * SourceRef, const EChannelState OldState, const EChannelState NewState )
|
|
{
|
|
TChannelLink * LinkChannel;
|
|
|
|
if (!Channel)
|
|
return false;
|
|
if (!(LinkChannel = GetLinkChannel( Channel, SourceRef )))
|
|
return false;
|
|
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Update Link Channel '%s'-->'%s' - State:%s",
|
|
ProcessName, Name, Channel->Name, LinkChannel->Channel->Ref, ChannelStateName[NewState] );
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
EChannelState CFunctionCore::ChannelOutState( TChannel * Channel, const char * TargetRef )
|
|
{
|
|
TChannelLink * LinkChannel = NULL;
|
|
EChannelState State = CH_off;
|
|
|
|
// Validate
|
|
if (!Channel)
|
|
return State;
|
|
|
|
// Check if any linked channels are ready
|
|
LinkChannel = Channel->FirstLink;
|
|
while (LinkChannel) {
|
|
if (!TargetRef || !*TargetRef || !strcasecmp( TargetRef, LinkChannel->Channel->Ref )) {
|
|
if (LinkChannel->Channel->State > State)
|
|
State = LinkChannel->Channel->State;
|
|
}
|
|
LinkChannel = LinkChannel->Next;
|
|
}
|
|
return State;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Automated Data Input/Output
|
|
bool CFunctionCore::LinkChannel( const char * ChannelName, const char * LinkFunctionName, const char * LinkChannelName, bool Input, bool Output )
|
|
{
|
|
TChannel * Channel = NULL;
|
|
CFunctionCore * LinkFunction = NULL;
|
|
TChannel * LinkChannel = NULL;
|
|
TChannelLink ** LinkedChannel = NULL;
|
|
|
|
// Check if Channels & Function exist
|
|
if (!(Channel = GetChannel( ChannelName )) ||
|
|
!(LinkFunction = Application->GetFunction( LinkFunctionName )) ||
|
|
!(LinkChannel = LinkFunction->GetChannel( LinkChannelName )) ) {
|
|
return false;
|
|
}
|
|
|
|
// Check if linked Channel exists
|
|
LinkedChannel = &(Channel->FirstLink);
|
|
while (*LinkedChannel && ((*LinkedChannel)->Channel != LinkChannel))
|
|
LinkedChannel = &((*LinkedChannel)->Next);
|
|
if (!*LinkedChannel)
|
|
*LinkedChannel = new TChannelLink;
|
|
|
|
// Set Parameters
|
|
(*LinkedChannel)->Function = LinkFunction;
|
|
(*LinkedChannel)->Channel = LinkChannel;
|
|
(*LinkedChannel)->Input = Input;
|
|
(*LinkedChannel)->Output = Output;
|
|
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Forward Channel Linked - '%s'-->'%s' - In:%s, Out:%s",
|
|
ProcessName, Name, Channel->Ref, (*LinkedChannel)->Channel->Ref, ((Input)? "Yes" : "No"), ((Output)? "Yes" : "No") );
|
|
|
|
// Find Linked channel on remote function
|
|
LinkedChannel = &(LinkChannel->FirstLink);
|
|
while (*LinkedChannel && ((*LinkedChannel)->Channel != Channel))
|
|
LinkedChannel = &((*LinkedChannel)->Next);
|
|
if (!*LinkedChannel)
|
|
*LinkedChannel = new TChannelLink;
|
|
|
|
// Set Parameters
|
|
(*LinkedChannel)->Function = this;
|
|
(*LinkedChannel)->Channel = Channel;
|
|
(*LinkedChannel)->Input = Output;
|
|
(*LinkedChannel)->Output = Input;
|
|
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Reverse Channel Linked - '%s'-->'%s' - In:%s, Out:%s",
|
|
ProcessName, Name, LinkChannel->Ref, (*LinkedChannel)->Channel->Ref, ((Output)? "Yes" : "No"), ((Input)? "Yes" : "No") );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CFunctionCore::UnlinkChannel( const char * ChannelName, const char * LinkFunctionName, const char * LinkChannelName )
|
|
{
|
|
TChannel * Channel = NULL;
|
|
CFunctionCore * LinkFunction = NULL;
|
|
TChannel * LinkChannel = NULL;
|
|
TChannelLink ** LinkedChannel = NULL;
|
|
TChannelLink * NextLinkedChannel = NULL;
|
|
|
|
// Check if Channels & Function exist
|
|
if (!(Channel = GetChannel( ChannelName )) ||
|
|
!(LinkFunction = Application->GetFunction( LinkFunctionName )) ||
|
|
!(LinkChannel = LinkFunction->GetChannel( LinkChannelName )) ) {
|
|
return false;
|
|
}
|
|
|
|
// Check if linked Channel exists
|
|
LinkedChannel = &(Channel->FirstLink);
|
|
while (*LinkedChannel && ((*LinkedChannel)->Channel != LinkChannel))
|
|
LinkedChannel = &((*LinkedChannel)->Next);
|
|
|
|
// Unlink channel
|
|
if (*LinkedChannel) {
|
|
if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Forward Channel Unlinked - '%s'-x->'%s'",
|
|
ProcessName, Name, Channel->Ref, (*LinkedChannel)->Channel->Ref );
|
|
NextLinkedChannel = (*LinkedChannel)->Next;
|
|
delete *LinkedChannel;
|
|
*LinkedChannel = NextLinkedChannel;
|
|
}
|
|
|
|
// Find Linked channel on remote function
|
|
LinkedChannel = &(LinkChannel->FirstLink);
|
|
while (*LinkedChannel && ((*LinkedChannel)->Channel != Channel))
|
|
LinkedChannel = &((*LinkedChannel)->Next);
|
|
|
|
// Unlink channel
|
|
if (*LinkedChannel) {
|
|
if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Reverse Channel Unlinked - '%s'-x->'%s'",
|
|
ProcessName, Name, LinkChannel->Ref, (*LinkedChannel)->Channel->Ref );
|
|
NextLinkedChannel = (*LinkedChannel)->Next;
|
|
delete *LinkedChannel;
|
|
*LinkedChannel = NextLinkedChannel;
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CFunctionCore::Input( const char * ChannelName, const char * SourceRef, const char * Data, int Len )
|
|
{
|
|
TChannel * Channel = NULL;
|
|
|
|
// Validate
|
|
if (!ChannelName || !*ChannelName || !Data) {
|
|
return 0;
|
|
}
|
|
|
|
// Get Channel
|
|
if (!(Channel = GetChannel( ChannelName ))) {
|
|
// Channel not found
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s'->'%s' - Input rejected, Channel not found",
|
|
ProcessName, Name, ((SourceRef && *SourceRef)? SourceRef : "(Any)"), ChannelName );
|
|
return 0;
|
|
}
|
|
else if (!Channel->State) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s'->'%s' - Input rejected, Channel %s",
|
|
ProcessName, Name, ((SourceRef && *SourceRef)? SourceRef : "(Any)"), ChannelName, ChannelStateName[Channel->State] );
|
|
return 0;
|
|
}
|
|
|
|
// Return processed bytes
|
|
if (Len == -1) {
|
|
Len = strlen( Data );
|
|
}
|
|
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Channel '%s'->'%s' - IN:",
|
|
ProcessName, Name, ((SourceRef && *SourceRef)? SourceRef : "(Any)"), ChannelName );
|
|
return Len;
|
|
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CFunctionCore::Output( const char * ChannelName, const char * TargetRef, const bool SourceRef, const char * Data, int Len )
|
|
{
|
|
TChannel * Channel = NULL;
|
|
|
|
// Validate
|
|
if (!ChannelName || !*ChannelName) {
|
|
return 0;
|
|
}
|
|
|
|
// Get Channel
|
|
if (!(Channel = GetChannel( ChannelName ))) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s'->'%s' - Output rejected, Output Channel not found",
|
|
ProcessName, Name, ChannelName, ((TargetRef && *TargetRef)? TargetRef : "(All)") );
|
|
return 0;
|
|
}
|
|
else {
|
|
// Return processed bytes
|
|
return Output( Channel, TargetRef, SourceRef, Data, Len );
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CFunctionCore::Output( const TChannel * Channel, const char * TargetRef, const bool SourceRef, const char * Data, int Len, int OutputFormat )
|
|
{
|
|
TChannelLink * LinkChannel = NULL;
|
|
int TempLen = 0;
|
|
int OutLen = 0;
|
|
|
|
// Validate
|
|
if (!Channel || !Data) {
|
|
return 0;
|
|
}
|
|
|
|
// Log event
|
|
if (Log) Log->Output( LogLevel, dlHigh, ((!OutputFormat)? LogOutput : OutputFormat), Data, Len, "%s/%s: Channel '%s'->'%s' - OUT:",
|
|
ProcessName, Name, Channel->Name, ((TargetRef && *TargetRef)? TargetRef : "(All)") );
|
|
|
|
// Pass output to all linked inputs
|
|
if (Len == -1)
|
|
Len = strlen( Data );
|
|
LinkChannel = Channel->FirstLink;
|
|
while (LinkChannel) {
|
|
if (!TargetRef || !*TargetRef || !strcasecmp( TargetRef, LinkChannel->Channel->Ref )) {
|
|
TempLen = LinkChannel->Function->Input( LinkChannel->Channel->Name, ((SourceRef)? Channel->Ref : NULL), Data, Len );
|
|
OutLen = (TempLen > OutLen)? TempLen : OutLen;
|
|
}
|
|
LinkChannel = LinkChannel->Next;
|
|
}
|
|
|
|
// Return processed bytes
|
|
return OutLen;
|
|
}
|
|
//---------------------------------------------------------------------------
|