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
This commit is contained in:
Charl Wentzel
2018-11-24 13:35:23 +02:00
parent 7434334280
commit a972fb9101
15 changed files with 518 additions and 469 deletions

View File

@@ -12,6 +12,7 @@
#include "ApplicationCore.h"
#include "WatchdogCore.h"
#include "FileCore.h"
#include "DeviceCore.h"
//---------------------------------------------------------------------------
@@ -25,13 +26,9 @@ CApplication::CApplication( EDebugLevel pLogLevel )
// Set signal handlers
ConfigureSignalHandlers();
// Variables used for configuration
ConfigMember = NULL;
FunctionConfigMember = NULL;
LinkConfigMember = NULL;
ConfigFile = NULL;
AddressFile = NULL;
DefinitionFile = NULL;
ConfigFile = NULL;
AddressFile = NULL;
// Create output logger
Log = new CLogCore( stdout );
@@ -39,31 +36,49 @@ CApplication::CApplication( EDebugLevel pLogLevel )
LogOutput = OUT_NORMAL;
// Create Configuration
DataTree = new CDataMember();
JSONparser = new CJSONparse( DataTree );
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;
Selector = NULL;
// List
FirstFunction = NULL;
FirstFunctionType = NULL;
FirstFunction = NULL;
// Add Core Function Types
//AddFunctionType( TYPE_FUNCTION, NewFunctionCore ); // <-- Can't add virtual function
AddFunctionType( TYPE_SELECTABLE, NewSelectableCore );
AddFunctionType( TYPE_WATCHDOG, NewWatchdogCore );
AddFunctionType( TYPE_FILE, NewFileCore );
//AddFunctionType( TYPE_DEVICE, NewDeviceCore ); // <-- Can't add virtual function
}
//---------------------------------------------------------------------------
CApplication::~CApplication()
{
TFunctionItem * NextFunction;
TFunctionType * NextType;
if (FirstFunction)
// Destroy functions
while (FirstFunction)
{
// Destroy functions
while (FirstFunction)
{
NextFunction = FirstFunction->Next;
delete( FirstFunction->Function );
free( FirstFunction );
FirstFunction = NextFunction;
}
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
@@ -98,7 +113,7 @@ void CApplication::GetProcessName( char ** ProcessName, char * pFilePath )
}
//---------------------------------------------------------------------------
bool CApplication::LoadConfig( int argc, char *argv[], const char * pConfigPath )
bool CApplication::ReadParam( int argc, char *argv[] )
{
// Read Parameters
if ((argc != 2))
@@ -116,16 +131,49 @@ bool CApplication::LoadConfig( int argc, char *argv[], const char * pConfigPath
// Load parameters
GetProcessName( &ProcessName, argv[1] );
ConfigFile = argv[1];
return true;
}
}
//---------------------------------------------------------------------------
// Load Application List
if (JSONparser->ReadFromFile( pConfigPath, ConfigFile )) {
if (Log) Log->Message( dlLow, dlLow, "%s: Config file loaded (%s)", ProcessName, ConfigFile );
} else {
if (Log) Log->Message( dlLow, dlLow, "%s: Fail to load config file (%s)- %s", ProcessName, ConfigFile, JSONparser->GetError() );
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;
}
//JSONparser->WriteToScreen( pConfigPath, 2 );
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;
@@ -136,79 +184,11 @@ bool CApplication::SaveConfig()
{
// Save updated configuration
if (ConfigFile && *ConfigFile)
JSONparser->WriteToFile( "config", ConfigFile );
JSONparser->WriteToFile( "Config", ConfigFile );
if (DefinitionFile && *DefinitionFile)
JSONparser->WriteToFile( "Definition", DefinitionFile );
if (AddressFile && *AddressFile )
JSONparser->WriteToFile( "address", AddressFile );
return true;
}
//---------------------------------------------------------------------------
bool CApplication::LoadConfigData()
{
CDataMember * TempMember;
EDebugLevel pLogLevel;
int pLogOutput;
char * TempStr;
// Get debug level
pLogLevel = dlNone;
TempStr = (char*)ConfigMember->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;
if ((TempMember = ConfigMember->GetChild( "Log/Output", true )))
{
TempMember = TempMember->GetFirstChild();
while (TempMember)
{
if ((TempStr = (char*)TempMember->GetStr()))
{
if (!strcasecmp( TempStr, "Normal"))
pLogOutput |= OUT_NORMAL;
else if (!strcasecmp( TempStr, "Bin"))
pLogOutput |= OUT_BIN;
else if (!strcasecmp( TempStr, "Hex"))
pLogOutput |= OUT_HEX;
else if (!strcasecmp( TempStr, "Count"))
pLogOutput |= OUT_COUNT;
else if (!strcasecmp( TempStr, "AsIs"))
pLogOutput |= OUT_ASIS;
else if (!strcasecmp( TempStr, "CRLF"))
pLogOutput |= OUT_CRLF;
}
// Next
TempMember = TempMember->GetNextPeer();
}
}
SetLogParam( pLogLevel, pLogOutput );
// Load Address List
if ((AddressFile = (char*)ConfigMember->GetChStr( "AddressList/Path", NULL )))
{
if (JSONparser->ReadFromFile( "address", AddressFile )) {
if (Log) Log->Message( dlLow, dlLow, "%s: Address file loaded (%s)", ProcessName, AddressFile );
} else {
if (Log) Log->Message( dlLow, dlLow, "%s: Fail to load Address file (%s) - %s", ProcessName, AddressFile, JSONparser->GetError() );
}
//JSONparser->WriteToScreen( pAddressListPath, 2 );
}
// Configure Selector
if ((TempMember = ConfigMember->GetChild( "Selector" )))
{
// Create Selector
Selector = new CSelect( (int)TempMember->GetChInt( "Wait", 5, true ), LogLevel );
}
JSONparser->WriteToFile( "AddressList", AddressFile );
return true;
}
//---------------------------------------------------------------------------
@@ -223,117 +203,220 @@ bool CApplication::SetLogParam( EDebugLevel pDebugLevel, int pOutputDisplay )
}
//---------------------------------------------------------------------------
bool CApplication::Init()
bool CApplication::InitApplication()
{
// Report status
if (Log) Log->Message( LogLevel, dlLow, "%s: Application Initialised", ProcessName );
CDataMember * CoreConfig;
CDataMember * ItemConfig;
CDataMember * SelectConfig;
EDebugLevel pLogLevel;
int pLogOutput;
char * TempStr;
return true;
}
//---------------------------------------------------------------------------
// Get Core definition
CoreConfig = Definition->GetChild( "Core", true );
bool CApplication::InitConfig( const char * pConfigPath )
{
// Check if Datatree exists
if (!DataTree || !(ConfigMember = DataTree->GetChild( pConfigPath ))) {
return false;
// 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;
}
// LoadConfiguration
LoadConfigData();
Init();
// 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::InitFunctions( const char * pConfigPath )
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;
CDataMember * TempMember;
char * Type;
// Check of path exists
if (!DataTree || !(FunctionConfigMember = DataTree->GetChild( pConfigPath )))
if (!(FunctionList = Definition->GetChild( "FunctionBlocks" ))) {
if (Log) Log->Message( LogLevel, dlLow, "%s: No Function Block specified", ProcessName );
return false;
}
// Process each Channel
TempMember = FunctionConfigMember->GetFirstChild();
while (TempMember)
FunctionConfig = FunctionList->GetFirstChild();
while (FunctionConfig)
{
// Get function parameters
Type = (char*)TempMember->GetChStr( "Type", "Custom", true );
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
if (!strcasecmp( Type, "WatchdogClient" )) {
Function = new CWatchdogCore( TempMember->GetName() );
}
else if (!strcasecmp( Type, "Selectable" )) {
Function = new CSelectableCore( TempMember->GetName() );
}
else if (!strcasecmp( Type, "File" )) {
Function = new CFileCore( TempMember->GetName() );
}
else {
Function = GetFunction( TempMember->GetName() );
}
Function = AddFunction( Type, FunctionConfig->GetName() );
// Load Function configuration and Initialise
if (Function) {
Function->InitConfig( TempMember );
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
TempMember = TempMember->GetNextPeer();
FunctionConfig = FunctionConfig->GetNextPeer();
}
return true;
}
//---------------------------------------------------------------------------
bool CApplication::InitFunctionLinks( const char * pConfigPath )
bool CApplication::InitFunctionLinks()
{
CDataMember * LinkList;
CDataMember * LinkConfig;
TFunctionItem * FunctionItem;
CDataMember * TempMember;
// Check of path exists
if (!DataTree || !(LinkConfigMember = DataTree->GetChild( pConfigPath )))
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 ((TempMember = LinkConfigMember->GetChild( FunctionItem->Function->GetName() )))
FunctionItem->Function->InitChannelLinks( TempMember );
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)
FunctionType = &((*FunctionType)->Next);
// 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
bool CApplication::AddFunction( CFunctionCore * Function )
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(), Function->GetName() ))
while (*FunctionPtr && strcasecmp( (*FunctionPtr)->Function->GetName(), Name ))
FunctionPtr = &((*FunctionPtr)->Next);
if (*FunctionPtr) return NULL; // Can't overwrite existing function
// Check if exists
if (*FunctionPtr)
return false;
// 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 = Function;
return true;
(*FunctionPtr)->Function = FunctionType->Constructor( Name );
return (*FunctionPtr)->Function;
}
//---------------------------------------------------------------------------