Files
redAcore/FunctionCore.cpp
Charl Wentzel a972fb9101 Major Update:
- Implement consistent Function addition to application
  - Use TYPE_XXX constants to declare function type
  - Use NewXXXX() methods to call constructor correctly
  - Add FunctionType list (with constructor) to Application
  - Create Function by comparison to FunctionType list
- Simplify LoadConfig() and Init() methods for functions
  - Combine methods into Init() method
  - Pass relevant data member to Init() method
  - Remove all CDataMember references on functions
- ApplicationCore:
  - Split ReadParam() method from LoadConfig() method
  - Split main configuration into separate files:
    - config/ - main config file, general application settings
    - definition/ - application definition, e.g. function blocks
  - Definition and Address List files specified in config file
  - Load address file in address/ branch
  - Made DataTree & JSONparser private
  - Made Config, Definition & Address branches public
  - Removed unnecessary branch references
  - Improved event logging
- DataTreeCore:
  - Allow GetChFirstChild & GetChElement to create parent branches
    with correct type, ie. Object/Array
  - Remove unnecessary Create param from GetXxx functions
  - Bug fix: Print empty objects/arrays correct, ie. empty brackets
  - Bug fix: Adding element at specific index
  - Bug fix: Error when get/create string value with "null"
- FunctionCore:
  - Type param now set as constant via constructor
  - Create empty Handles & Channels objects if none in Config
- SelectableCore:
  - Add Queue length parameter to handles for UNIX and TCP sockets
- DeviceCore:
  - Bug fix: missing Process() method
2018-11-24 13:35:23 +02:00

537 lines
17 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 = (char*)malloc( strlen(pName)+1 );
strcpy( Name, pName );
}
else {
Name = NULL;
}
// Channels
FirstChannel = NULL;
// Logging
Log = Application->Log;
LogLevel = dlNone;
LogOutput = OUT_NORMAL;
// Stored output
StoredOutput = NULL;
StoredOutputLen = 0;
}
//---------------------------------------------------------------------------
CFunctionCore::~CFunctionCore()
{
TChannel * NextChannel = NULL;
TChannelLink * NextLinkedChannel = NULL;
// Destroy Channels
while (FirstChannel)
{
// Destroy Parameters
if (FirstChannel->Name) {
free( FirstChannel->Name );
}
// Destroy Linked Inputs
while (FirstChannel->FirstInput) {
if (FirstChannel->FirstInput->Name) {
free( FirstChannel->FirstInput->Name );
}
NextLinkedChannel = FirstChannel->FirstInput->Next;
free( FirstChannel->FirstInput );
FirstChannel->FirstInput = NextLinkedChannel;
}
// Destroy Linked Outputs
while (FirstChannel->FirstOutput) {
if (FirstChannel->FirstOutput->Name) {
free( FirstChannel->FirstOutput->Name );
}
NextLinkedChannel = FirstChannel->FirstOutput->Next;
free( FirstChannel->FirstOutput );
FirstChannel->FirstOutput = NextLinkedChannel;
}
// Destroy Channel
NextChannel = FirstChannel->Next;
free( 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 * ItemConfig;
CDataMember * ChannelConfig;
EDebugLevel pLogLevel;
int pLogOutput;
char * TempStr;
// Validate
if (!FunctionConfig )
return false;
// Get debug level
pLogLevel = dlNone;
TempStr = (char*)FunctionConfig->GetChStr( "Log/Level", "Medium", true );
if (TempStr)
{
if (!strcasecmp( TempStr, "Low" ))
pLogLevel = dlLow;
else if (!strcasecmp( TempStr, "Medium" ))
pLogLevel = dlMedium;
else if (!strcasecmp( TempStr, "High" ))
pLogLevel = dlHigh;
}
// Set debug output
pLogOutput = 0;
ItemConfig = FunctionConfig->GetChild( "Log/Output[0]", true ); // Create first element if not exist
while (ItemConfig)
{
if ((TempStr = (char*)ItemConfig->GetStr( "Normal" ))) // Set default value "Normal" if not set
{
if (!strcasecmp( TempStr, "Normal")) // Normal ASCII -> Unless "AsIs", print special chars as "."
pLogOutput |= OUT_NORMAL;
else if (!strcasecmp( TempStr, "Bin")) // Print as Binary value ('1' and '0')
pLogOutput |= OUT_BIN;
else if (!strcasecmp( TempStr, "Hex")) // Print as Hexadecimal value (0-9,A-F)
pLogOutput |= OUT_HEX;
else if (!strcasecmp( TempStr, "Count")) // Print number of characters
pLogOutput |= OUT_COUNT;
else if (!strcasecmp( TempStr, "AsIs")) // Use with "Normal" -> Do not print special chars as "."
pLogOutput |= OUT_ASIS;
else if (!strcasecmp( TempStr, "CRLF")) // Add \r\n at end of line if not present
pLogOutput |= OUT_CRLF;
else
pLogOutput |= OUT_NORMAL;
}
ItemConfig = ItemConfig->GetNextPeer();
}
SetLogParam( pLogLevel, pLogOutput );
// Load Channels
ChannelConfig = FunctionConfig->GetChFirstChild( "Channels", true );
while (ChannelConfig)
{
if (ChannelConfig->GetName()) {
AddChannel( ChannelConfig->GetName(),
ChannelConfig->GetChBool( "InputEnabled", true, true ),
ChannelConfig->GetChBool( "OutputEnabled", false, true ));
}
ChannelConfig = ChannelConfig->GetNextPeer();
}
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
LinkOutputChannel( Channel->Name,
FunctionMember->GetChStr( "Function" ),
FunctionMember->GetChStr( "Channel" ),
FunctionMember->GetChBool( "Bidirectional" ) );
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 bool pInputEnable, const bool pOutputEnable )
{
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 = (TChannel*)malloc( sizeof(TChannel) );
memset( *Channel, 0, sizeof(TChannel) );
// Set Name
(*Channel)->Name = (char*)malloc( strlen(ChannelName)+1 );
strcpy( (*Channel)->Name, ChannelName );
// Log Event
if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Channel '%s' - Created",
ProcessName, Name, ChannelName );
}
// Set parameters
(*Channel)->InputEnabled = pInputEnable;
(*Channel)->OutputEnabled = pOutputEnable;
return *Channel;
}
//---------------------------------------------------------------------------
// Automated Data Input/Output
bool CFunctionCore::LinkInputChannel( const char * ChannelName, const char * OutFunctionName, const char * OutChannelName, bool Bidirectional )
{
CFunctionCore * OutFunction = NULL;
TChannel * Channel = NULL;
TChannelLink ** LinkedChannel = NULL;
// Get Channel
if (!(OutFunction = Application->GetFunction( OutFunctionName )) || !(Channel = GetChannel( ChannelName ))) {
return false;
}
// Check if linked Channel exists
LinkedChannel = &(Channel->FirstInput);
while (*LinkedChannel && (((*LinkedChannel)->Function != OutFunction) || strcmp( (*LinkedChannel)->Name, OutChannelName ) )) {
LinkedChannel = &((*LinkedChannel)->Next);
}
// Create if not found
if (!*LinkedChannel)
{
// Create
*LinkedChannel = (TChannelLink*)malloc( sizeof(TChannelLink) );
memset( *LinkedChannel, 0, sizeof( TChannelLink ));
// Set Parameters
(*LinkedChannel)->Function = OutFunction;
(*LinkedChannel)->Name = (char*)malloc( strlen(OutChannelName)+1 );
strcpy( (*LinkedChannel)->Name, OutChannelName );
// Log Event
if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Input Linked - '%s'/'%s' <-- '%s'/'%s'",
ProcessName, Name, Name, ChannelName, OutFunction->GetName(), OutChannelName );
}
// Link Return direction as well
if (Bidirectional) {
return OutFunction->LinkInputChannel( OutChannelName, Name, ChannelName, false );
}
return true;
}
//---------------------------------------------------------------------------
bool CFunctionCore::LinkOutputChannel( const char * ChannelName, const char * InFunctionName, const char * InChannelName, bool Bidirectional )
{
TChannel * OutChannel = NULL;
CFunctionCore * InFunction = NULL;
TChannel * InChannel = NULL;
TChannelLink ** LinkedChannel = NULL;
// Check if Channels & Function exist
if (!(OutChannel = GetChannel( ChannelName )) ||
!(InFunction = Application->GetFunction( InFunctionName )) ||
!(InChannel = InFunction->GetChannel( InChannelName )) ) {
return false;
}
// Check if linked Channel exists
LinkedChannel = &(OutChannel->FirstOutput);
while (*LinkedChannel && (((*LinkedChannel)->Function != InFunction) || strcmp( (*LinkedChannel)->Name, InChannelName ) )) {
LinkedChannel = &((*LinkedChannel)->Next);
}
// Create if not found
if (!*LinkedChannel)
{
// Create
*LinkedChannel = (TChannelLink*)malloc( sizeof(TChannelLink) );
memset( *LinkedChannel, 0, sizeof( TChannelLink ));
// Set Parameters
(*LinkedChannel)->Function = InFunction;
(*LinkedChannel)->Name = (char*)malloc( strlen(InChannelName)+1 );
strcpy( (*LinkedChannel)->Name, InChannelName );
// Log Event
if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Output Linked - '%s'/'%s' --> '%s'/'%s'",
ProcessName, Name, Name, ChannelName, InFunction->GetName(), InChannelName );
}
// Link return direction as well
if (Bidirectional) {
return InFunction->LinkOutputChannel( InChannelName, Name, ChannelName, false );
}
return true;
}
//---------------------------------------------------------------------------
// Manual Data Input/Output
int CFunctionCore::Input( const char * ChannelName, const char * Data, int Len )
{
TChannel * Channel = NULL;
// Validate
if (!ChannelName || !Data) {
return 0;
}
// Get Channel
if (!(Channel = GetChannel( ChannelName ))) {
// Channel not found
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Input rejected, Channel not found",
ProcessName, Name, ChannelName );
return 0;
}
else if (!Channel->InputEnabled) {
// Channel disabled
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Input rejected, Channel input disabled",
ProcessName, Name, ChannelName );
return 0;
}
else {
// Return processed bytes
if (Len == -1) {
Len = strlen( Data );
}
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Channel '%s' - IN:",
ProcessName, Name, ChannelName );
return Len;
}
}
//---------------------------------------------------------------------------
int CFunctionCore::Output( const char * ChannelName, const char * Data, int Len )
{
TChannel * Channel = NULL;
// Validate
if (!ChannelName) {
return 0;
}
// Get Channel
if (!(Channel = GetChannel( ChannelName ))) {
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Output rejected, Channel not found",
ProcessName, Name, ChannelName );
return 0;
}
else {
// Return processed bytes
return Output( Channel, Data, Len );
}
}
//---------------------------------------------------------------------------
int CFunctionCore::Output( const TChannel * Channel, const char * Data, int Len )
{
TChannelLink * OutChannel = NULL;
int TempLen = 0;
int OutLen = 0;
// Validate
if (!Channel || !Data) {
return 0;
}
// Check if enabled
if (!Channel->OutputEnabled) {
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Output rejected, Channel output disabled",
ProcessName, Name, Channel->Name );
return 0;
}
// Log event
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Channel '%s' - OUT:",
ProcessName, Name, Channel->Name );
// Pass output to all linked inputs
if (Len == -1) {
Len = strlen( Data );
}
OutChannel = Channel->FirstOutput;
while (OutChannel) {
TempLen = OutChannel->Function->Input( OutChannel->Name, Data, Len );
OutLen = (TempLen > OutLen)? TempLen : OutLen;
OutChannel = OutChannel->Next;
}
// Return processed bytes
return OutLen;
}
//---------------------------------------------------------------------------
bool CFunctionCore::PullInput( const char * ChannelName )
{
TChannel * Channel = NULL;
// Validate
if (!ChannelName) {
return false;
}
// Get Channel
if (!(Channel = GetChannel( ChannelName ))) {
// Channel not found
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Input failed, Channel not found",
ProcessName, Name, ChannelName );
return false;
}
else {
// Return success
return PullInput( Channel );
}
}
//---------------------------------------------------------------------------
bool CFunctionCore::PullInput( TChannel * Channel )
{
TChannelLink * InChannel = NULL;
char ** Data = NULL;
int * Len = NULL;
// Validate
if (!Channel) {
return false;
}
// Check if enabled
if (!Channel->InputEnabled) {
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Input failed, Channel input disabled",
ProcessName, Name, Channel->Name );
return false;
}
// Pass output to all linked inputs
InChannel = Channel->FirstInput;
while (InChannel)
{
// Pull Output from Channel
if (InChannel->Function->PullOutput( InChannel->Name, Data, Len ))
{
// Use input
Input( Channel->Name, *Data, ((*Len)? *Len : -1) );
}
InChannel = InChannel->Next;
}
// Return success
return Len;
}
//---------------------------------------------------------------------------
bool CFunctionCore::PullOutput( const char * ChannelName, char ** Data, int * Len )
{
TChannel * Channel = NULL;
int TempLen = 0;
// Validate
if (!ChannelName || !Data) {
return 0;
}
// Get Channel
if (!(Channel = GetChannel( ChannelName ))) {
// Channel not found
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Output failed, Channel not found",
ProcessName, Name, ChannelName );
return 0;
}
else if (!Channel->InputEnabled) {
// Channel disabled
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Output failed, Channel output disabled",
ProcessName, Name, ChannelName );
return 0;
}
else {
// Return processed bytes
*Data = StoredOutput;
TempLen = (*Data)? strlen(*Data) : 0;
if (Len) *Len = TempLen;
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, ((*Data)? *Data : "(NULL)"), TempLen, "%s/%s: Channel '%s' - IN:",
ProcessName, Name, ChannelName );
return Len;
}
}
//---------------------------------------------------------------------------