Files
redAcore/ApplicationCore.cpp
Charl Wentzel 889df6c7de Important Update:
- DeviceCore:
  - Added PollCycle variable (not used here)
- WatchdogCore:
  - Return "true" on in Process()
- SelectableCore:
  - Minor update (code compacted)
- Application/Function:
  - Added CApplication as friend of CFunction
  - Added param WaitToTerminate on Function
  - Application Run() only exists if all (WaitToTerminate) functions
    has terminated cleanly.
2019-03-09 20:41:25 +02:00

435 lines
14 KiB
C++

/*
* FunctionListCore.cpp
*
* Created on: 12 Jul 2017
* Author: wentzelc
*/
// Standard C/C++ Libraries
/* none */
// redA Libraries
#include "ApplicationCore.h"
#include "WatchdogCore.h"
#include "FileCore.h"
#include "DeviceCore.h"
//---------------------------------------------------------------------------
// Global Vars
extern bool Terminate;
extern char * ProcessName;
//---------------------------------------------------------------------------
CApplication::CApplication( EDebugLevel pLogLevel )
{
// Set signal handlers
ConfigureSignalHandlers();
DefinitionFile = NULL;
ConfigFile = NULL;
AddressFile = NULL;
// Create output logger
Log = new CLogCore( stdout );
LogLevel = pLogLevel;
LogOutput = loNormal;
// Create Configuration
DataTree = new CDataMember();
Config = DataTree->GetChild( "Config", true );
Definition = DataTree->GetChild( "Definition", true );
AddressList = DataTree->GetChild( "AddressList", true );
JSONparser = new CJSONparse( DataTree );
// Selector
Selector = NULL;
// List
FirstFunctionType = NULL;
FirstFunction = NULL;
// Add Core Function Types
//AddFunctionType( TYPE_FUNCTION, NewFunctionCore ); // <-- Can't add virtual function
AddFunctionType( TYPE_SELECTABLE, NewSelectableCore );
AddFunctionType( TYPE_WATCHDOGPING, NewWatchdogCore );
AddFunctionType( TYPE_FILE, NewFileCore );
//AddFunctionType( TYPE_DEVICE, NewDeviceCore ); // <-- Can't add virtual function
}
//---------------------------------------------------------------------------
CApplication::~CApplication()
{
TFunctionItem * NextFunction;
TFunctionType * NextType;
// Destroy functions
while (FirstFunction)
{
NextFunction = FirstFunction->Next;
delete( FirstFunction->Function );
free( FirstFunction );
FirstFunction = NextFunction;
}
// Destroy function types
while (FirstFunctionType)
{
NextType = FirstFunctionType->Next;
free( FirstFunctionType->Name );
free( FirstFunctionType );
FirstFunctionType = NextType;
}
// Show Completion
if (Log) Log->Message( dlLow, dlLow, "%s: Terminated.", ProcessName );
// Destroy tools
if (Selector) delete Selector;
if (Log) delete Log;
if (DataTree) delete DataTree;
if (JSONparser) delete JSONparser;
// Free Process Name
if (ProcessName) free( ProcessName );
}
//---------------------------------------------------------------------------
void CApplication::GetProcessName( char ** ProcessName, char * pFilePath )
{
char * TempStr;
// Remove Path
TempStr = strrchr( pFilePath, '/' );
if (TempStr[0] == '/') TempStr++;
// Copy Process Name
*ProcessName = strdup( TempStr );
// Remove extension
TempStr = strrchr( *ProcessName, '.' );
if (TempStr) TempStr[0] = 0;
}
//---------------------------------------------------------------------------
bool CApplication::ReadParam( int argc, char *argv[] )
{
// Read Parameters
if ((argc != 2))
{
// Get Process Name
GetProcessName( &ProcessName, argv[0] );
// Incorrect no of parameters, show correct usage
printf( "%s: Incorrect number of parameters\n", ProcessName );
printf( " usage: %s [ConfigFile]\n\n", ProcessName );
return false;
}
else
{
// Load parameters
GetProcessName( &ProcessName, argv[1] );
ConfigFile = argv[1];
return true;
}
}
//---------------------------------------------------------------------------
bool CApplication::LoadConfig()
{
// Load Configuration Data
if (!JSONparser->ReadFromFile( "Config", ConfigFile )) {
if (Log) Log->Message( dlLow, dlLow, "%s: Fail to load Configuration file (%s) - %s", ProcessName, ConfigFile, JSONparser->GetError() );
return false;
}
else {
//JSONparser->WriteToScreen( "config", 2 );
if (Log) Log->Message( dlLow, dlLow, "%s: Configuration file loaded (%s)", ProcessName, ConfigFile );
}
// Load Application Definition
if (!(DefinitionFile = (char*)Config->GetChStr( "Application/Definition" ))) {
if (Log) Log->Message( dlLow, dlLow, "%s: No Application Definition file specified", ProcessName );
return false;
}
else if (!JSONparser->ReadFromFile( "Definition", DefinitionFile )) {
if (Log) Log->Message( dlLow, dlLow, "%s: Fail to load Application Definition file (%s) - %s", ProcessName, DefinitionFile, JSONparser->GetError() );
return false;
}
else {
//JSONparser->WriteToScreen( "application", 2 );
if (Log) Log->Message( dlLow, dlLow, "%s: Application Definition file loaded (%s)", ProcessName, DefinitionFile );
}
// Load Address List
if (!(AddressFile = (char*)Config->GetChStr( "Application/Addresses/List" ))) {
if (Log) Log->Message( dlLow, dlLow, "%s: No Address List file specified", ProcessName );
}
else if (!JSONparser->ReadFromFile( "AddressList", AddressFile )) {
if (Log) Log->Message( dlLow, dlLow, "%s: Fail to load Address List file (%s) - %s", ProcessName, AddressFile, JSONparser->GetError() );
return false;
}
else {
//JSONparser->WriteToScreen( "address", 2 );
if (Log) Log->Message( dlLow, dlLow, "%s: Address List file loaded (%s)", ProcessName, AddressFile );
}
// Loaded successfully
return true;
}
//---------------------------------------------------------------------------
bool CApplication::SaveConfig()
{
// Save updated configuration
if (ConfigFile && *ConfigFile)
JSONparser->WriteToFile( "Config", ConfigFile );
if (DefinitionFile && *DefinitionFile)
JSONparser->WriteToFile( "Definition", DefinitionFile );
if (AddressFile && *AddressFile )
JSONparser->WriteToFile( "AddressList", AddressFile );
return true;
}
//---------------------------------------------------------------------------
bool CApplication::SetLogParam( EDebugLevel pDebugLevel, int pOutputDisplay )
{
// Output
LogLevel = pDebugLevel;
LogOutput = pOutputDisplay;
return true;
}
//---------------------------------------------------------------------------
bool CApplication::InitApplication()
{
CDataMember * CoreConfig;
CDataMember * LogConfig;
CDataMember * SelectConfig;
EDebugLevel pLogLevel;
int pLogOutput;
// Get Core definition
CoreConfig = Definition->GetChild( "Core", true );
// Configure Logging/Debugging
LogConfig = CoreConfig->GetChild( "Log", true );
pLogLevel = Log->ReadLogLevel( LogConfig );
pLogOutput = Log->ReadLogOutput( LogConfig );
SetLogParam( pLogLevel, pLogOutput );
// Configure Selector
if ((SelectConfig = CoreConfig->GetChild( "Selector" ))) {
Selector = new CSelect( (int)SelectConfig->GetChInt( "Wait", 5, true ), LogLevel );
}
// Show status
if (Log) Log->Message( LogLevel, dlLow, "%s: Application Core configured.", ProcessName );
return true;
}
//---------------------------------------------------------------------------
bool CApplication::InitFunction( CFunctionCore * Function )
{
char FunctionPath[100];
CDataMember * FunctionConfig;
// Validate
if (!Function)
return false;
// Load configuration
sprintf( FunctionPath, "FunctionBlocks/%s", Function->GetName() );
if (!(FunctionConfig = Definition->GetChild( FunctionPath, true ))) {
if (Log) Log->Message( LogLevel, dlMedium, "%s: Function '%s' - Type '%s' cannot create config!",
ProcessName, Function->GetName(), Function->GetType() );
}
// Configure Function
else if (!Function->Init( FunctionConfig )) {
if (Log) Log->Message( LogLevel, dlMedium, "%s: Function '%s' - Type '%s' init failed!",
ProcessName, Function->GetName(), Function->GetType() );
return false;
}
else {
if (Log) Log->Message( LogLevel, dlMedium, "%s: Function '%s' - Type '%s' configured.",
ProcessName, Function->GetName(), Function->GetType() );
}
return true;
}
//---------------------------------------------------------------------------
bool CApplication::InitFunctions()
{
CDataMember * FunctionList;
CDataMember * FunctionConfig;
CFunctionCore * Function;
char * Type;
// Check of path exists
if (!(FunctionList = Definition->GetChild( "FunctionBlocks" ))) {
if (Log) Log->Message( LogLevel, dlLow, "%s: No Function Block specified", ProcessName );
return false;
}
// Process each Function
FunctionConfig = FunctionList->GetFirstChild();
while (FunctionConfig)
{
// Get function parameters
Type = (char*)FunctionConfig->GetChStr( "Type", "Custom", true );
if (Log) Log->Message( LogLevel, dlLow, "%s: Function '%s' - Type '%s' initialising...",
ProcessName, FunctionConfig->GetName(), Type );
// Get or create function
Function = AddFunction( Type, FunctionConfig->GetName() );
// Load Function configuration and Initialise
if (!Function) {
if (Log) Log->Message( LogLevel, dlMedium, "%s: Function '%s' - Type '%s' not found!",
ProcessName, FunctionConfig->GetName(), Type );
}
else if (!Function->Init( FunctionConfig )) {
if (Log) Log->Message( LogLevel, dlMedium, "%s: Function '%s' - Type '%s' init failed!",
ProcessName, Function->GetName(), Function->GetType() );
}
else {
if (Log) Log->Message( LogLevel, dlMedium, "%s: Function '%s' - Type '%s' configured.",
ProcessName, Function->GetName(), Function->GetType() );
}
// Next
FunctionConfig = FunctionConfig->GetNextPeer();
}
return true;
}
//---------------------------------------------------------------------------
bool CApplication::InitFunctionLinks()
{
CDataMember * LinkList;
CDataMember * LinkConfig;
TFunctionItem * FunctionItem;
// Check of path exists
if (!(LinkList = Definition->GetChild( "ChannelLinks", true ))) {
if (Log) Log->Message( LogLevel, dlMedium, "%s: No Function Channel links specified.", ProcessName );
return false;
}
else {
if (Log) Log->Message( LogLevel, dlLow, "%s: Linking Function Block Channels...", ProcessName );
}
// Process each Channel
FunctionItem = FirstFunction;
while (FunctionItem)
{
// Build links for function
if ((LinkConfig = LinkList->GetChild( FunctionItem->Function->GetName() )))
FunctionItem->Function->InitChannelLinks( LinkConfig );
// Next
FunctionItem = FunctionItem->Next;
}
if (Log) Log->Message( LogLevel, dlLow, "%s: Function Block Channels linked.", ProcessName );
return true;
}
//---------------------------------------------------------------------------
bool CApplication::AddFunctionType( const char * Type, FFuncConstructor Constructor )
{
TFunctionType ** FunctionType;
// Get end of list
FunctionType = &FirstFunctionType;
while (*FunctionType && strcasecmp(Type, (*FunctionType)->Name))
FunctionType = &((*FunctionType)->Next);
if (*FunctionType) {
if (Log) Log->Message( dlLow, dlLow, "%s: Cannot add FunctionType '%s' - Already exists",
ProcessName, Type );
return NULL;
}
// Add new Type
*FunctionType = (TFunctionType*)calloc( sizeof(TFunctionType), 1 );
(*FunctionType)->Name = strdup( Type );
(*FunctionType)->Constructor = Constructor;
return true;
};
//---------------------------------------------------------------------------
// Build List
CFunctionCore * CApplication::AddFunction( const char * Type, const char * Name )
{
TFunctionItem ** FunctionPtr;
TFunctionType * FunctionType;
// Check if unique, else get end of list
FunctionPtr = &FirstFunction;
while (*FunctionPtr && strcasecmp( (*FunctionPtr)->Function->GetName(), Name ))
FunctionPtr = &((*FunctionPtr)->Next);
if (*FunctionPtr) return NULL; // Can't overwrite existing function
// Check if type is valid
FunctionType = FirstFunctionType;
while (FunctionType && strcasecmp( FunctionType->Name, Type ))
FunctionType = FunctionType->Next;
if (!FunctionType) return NULL; // Type not defined
// Add to end of list
*FunctionPtr = (TFunctionItem*)calloc( sizeof(TFunctionItem), 1 );
(*FunctionPtr)->Function = FunctionType->Constructor( Name );
return (*FunctionPtr)->Function;
}
//---------------------------------------------------------------------------
// Search List
CFunctionCore * CApplication::GetFunction( const char * Name )
{
TFunctionItem * FunctionItem;
// Find in list
FunctionItem = FirstFunction;
while (FunctionItem && strcasecmp( FunctionItem->Function->GetName(), Name ))
FunctionItem = FunctionItem->Next;
return ((FunctionItem)? FunctionItem->Function : NULL);
}
//---------------------------------------------------------------------------
bool CApplication::Run( bool TerminateOnError )
{
bool CleanTerminate = true;
bool ProcessTerminate = false;
TFunctionItem * FunctionItem;
// Check for FD Events/States
if (Selector) {
Selector->Test();
}
// Process Functions
for (FunctionItem = FirstFunction; FunctionItem; FunctionItem = FunctionItem->Next )
{
ProcessTerminate = !FunctionItem->Function->Process();
if (TerminateOnError) {
if (ProcessTerminate)
Terminate = true;
if (FunctionItem->Function->WaitToTerminate && !ProcessTerminate)
CleanTerminate = false;
}
}
if (Terminate && TerminateOnError && CleanTerminate)
return false;
else
return true;
}
//---------------------------------------------------------------------------