- WatchdogCore: - Renamed FunctionType TYPE_WATCHDOG -> TYPE_WATCHDOGPING - ApplicationCore: - Prevent overwriting existing Function Type
467 lines
16 KiB
C++
467 lines
16 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 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 = OUT_NORMAL;
|
|
|
|
// 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 = (char*)malloc( strlen(TempStr)+1 );
|
|
strcpy( *ProcessName, 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( "DefinitionFile" ))) {
|
|
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( "AddressFile" ))) {
|
|
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 * ItemConfig;
|
|
CDataMember * SelectConfig;
|
|
EDebugLevel pLogLevel;
|
|
int pLogOutput;
|
|
char * TempStr;
|
|
|
|
// Get Core definition
|
|
CoreConfig = Definition->GetChild( "Core", true );
|
|
|
|
// Get debug level
|
|
pLogLevel = dlNone;
|
|
TempStr = (char*)CoreConfig->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 = CoreConfig->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 );
|
|
|
|
// 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 = NULL;
|
|
|
|
// 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 Channel
|
|
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 = (char*)malloc( strlen(Type)+1 );
|
|
strcpy( (*FunctionType)->Name, 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 AllGood = true;
|
|
TFunctionItem * FunctionItem;
|
|
|
|
// Check for FD Events/States
|
|
if (Selector) {
|
|
Selector->Test();
|
|
}
|
|
|
|
// Process Functions
|
|
for (FunctionItem = FirstFunction; FunctionItem; FunctionItem = FunctionItem->Next )
|
|
{
|
|
if (!FunctionItem->Function->Process())
|
|
{
|
|
if (TerminateOnError)
|
|
return false;
|
|
else {
|
|
AllGood = false;
|
|
}
|
|
}
|
|
}
|
|
return AllGood;
|
|
}
|
|
//---------------------------------------------------------------------------
|