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:
@@ -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;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user