/* * FunctionCore.cpp * * Created on: 18 May 2016 * Author: wentzelc */ //--------------------------------------------------------------------------- // Standard C/C++ Libraries #include #include #include #include #include #include // redA Libraries #include "DeviceCore.h" #include "FunctionCore.h" #include "TimingCore.h" #include "LogCore.h" //--------------------------------------------------------------------------- CDeviceCore::CDeviceCore( const char * Name, CLogCore * pLog, EDebugLevel DebugLevel, int pOutputDisplay ) : CFunctionCore( Name, pLog, DebugLevel, pOutputDisplay ) { // Clear Parameters FirstDevice = NULL; ActiveDevice = NULL; // Standard channels DeviceChannel = NULL; CmdChannel = NULL; // Polling PollStep = 0; PollInterval = 250; SetStartTime( &PollWait ); WaitingForReply = false; ReplyTimeout = 200; PollRetry = 0; MaxRetries = 3; // Update Timer SetUpdate( 0 ); } //--------------------------------------------------------------------------- CDeviceCore::~CDeviceCore() { // Destroy Params while (FirstDevice) DestroyDevice( &FirstDevice ); } //--------------------------------------------------------------------------- bool CDeviceCore::SetPollParam( int pPollInterval ) { PollInterval = pPollInterval; return true; } //--------------------------------------------------------------------------- bool CDeviceCore::SetReplyParam( int pReplyTimeout, int pMaxRetries ) { ReplyTimeout = pReplyTimeout; MaxRetries = pMaxRetries; return true; } //--------------------------------------------------------------------------- // Generate events bool CDeviceCore::SetUpdate( int pUpdateInterval ) { UpdateInterval = pUpdateInterval; SetStartTime( &UpdateTimeout ); return true; } //--------------------------------------------------------------------------- bool CDeviceCore::DeviceOnline( TDevice * Device, bool Online ) { // Validate if (!Device) return false; // Check if change of state if (Device->Online == Online) return true; // Log Event Device->Online = Online; Log->Message( DebugLevel, dlHigh, "%s: Device '%s' %s", Name, Device->Name, ((Online)? "online" : "offline") ); return true; } //--------------------------------------------------------------------------- void CDeviceCore::SetWaitForReply() { // Start timer SetStartTime( &PollWait ); // Set flag WaitingForReply = true; } //--------------------------------------------------------------------------- bool CDeviceCore::CheckReplyTimeout( int TimeoutPollStep ) { // Check for Reply timeout if (WaitingForReply && Timeout( PollWait, ReplyTimeout )) { // Increment retries PollRetry++; // Handle No Reply / Retry if (PollRetry < MaxRetries) { // Log Event Log->Message( DebugLevel, dlHigh, "%s: Channel '%s' - %s timeout, retry [%d]", Name, DeviceChannel->Name, ActiveDevice->Name, PollRetry ); } else { // Log Event Log->Message( DebugLevel, dlHigh, "%s: Channel '%s' - %s timeout, max [%d]", Name, DeviceChannel->Name, ActiveDevice->Name, PollRetry ); // Set Device Offline DeviceOnline( ActiveDevice, false ); PollStep = TimeoutPollStep; } // Reset flag - retry WaitingForReply = false; return true; } else { // Reset retries PollRetry = 0; return false; } } //--------------------------------------------------------------------------- TDevice * CDeviceCore::AddDevice( const char * DeviceName ) { // Get register or end of list TDevice ** Device = &FirstDevice; while (*Device && strcmp( DeviceName, (*Device)->Name )) Device = &((*Device)->Next); // Create if not found if (!*Device) { // Create register *Device = (TDevice*)malloc( sizeof(TDevice) ); memset( *Device, 0x0, sizeof(TDevice) ); // Set Name (*Device)->Name = (char *)malloc( strlen( DeviceName )+1 ); strcpy( (*Device)->Name, DeviceName ); } // Report creation Log->Message( DebugLevel, dlLow, "%s: Device added - '%s'", Name, DeviceName ); return *Device; } //--------------------------------------------------------------------------- bool CDeviceCore::DestroyDevice( const char * DeviceName ) { TDevice ** Device = NULL; // Find device if (!(Device = GetDevicePtr(DeviceName))) return false; // Destroy Device if (!DestroyDevice( Device )) return false; // Device destroyed return true; } //--------------------------------------------------------------------------- bool CDeviceCore::DestroyDevice( TDevice ** Device ) { TDevice * NextDevice = NULL; // Validate param if (!Device || !*Device) return false; // Save reference to next Device NextDevice = (*Device)->Next; // Destroy Device structure if ((*Device)->Name) free( (*Device)->Name ); // Destroy paramters while ((*Device)->FirstParam) { DestroyDeviceParam( &((*Device)->FirstParam) ); } // Destory Device free( *Device ); // Remove from list *Device = NextDevice; // Device successfully destroyed return true; } //--------------------------------------------------------------------------- TDeviceParam * CDeviceCore::AddDeviceParam( TDevice * Device, const char * ParamName, EMBDataType DataType, int ParamLen ) { // Get register or end of list TDeviceParam ** Param = &Device->FirstParam; while (*Param && strcmp( ParamName, (*Param)->Name )) Param = &((*Param)->Next); // Create if not found if (!*Param) { // Create register *Param = (TDeviceParam*)malloc( sizeof(TDeviceParam) ); memset( *Param, 0x0, sizeof(TDeviceParam) ); // Set Name (*Param)->Name = (char *)malloc( strlen( ParamName )+1 ); strcpy( (*Param)->Name, ParamName ); (*Param)->DataType = DataType; // Init values switch (DataType) { case dtUnsigned16 : // Create Value pointer (*Param)->Value = (u_int16_t*)malloc( sizeof(u_int16_t) ); *((u_int16_t*)(*Param)->Value) = 0; (*Param)->Len = sizeof(u_int16_t); // Create Set Value pointer (*Param)->SetValue = (u_int16_t*)malloc( sizeof(u_int16_t) ); *((u_int16_t*)(*Param)->SetValue) = 0; (*Param)->SetLen = sizeof(u_int16_t); break; case dtSigned16 : // Create Value pointer (*Param)->Value = (int16_t*)malloc( sizeof(int16_t) ); *((int16_t*)(*Param)->Value) = 0; (*Param)->Len = sizeof(int16_t); // Create Set Value pointer (*Param)->SetValue = (int16_t*)malloc( sizeof(int16_t) ); *((int16_t*)(*Param)->SetValue) = 0; (*Param)->SetLen = sizeof(int16_t); break; case dtUnsigned32 : // Create Value pointer (*Param)->Value = (u_int32_t*)malloc( sizeof(u_int32_t) ); *((u_int32_t*)(*Param)->Value) = 0; (*Param)->Len = sizeof(u_int32_t); // Create Set Value pointer (*Param)->SetValue = (u_int32_t*)malloc( sizeof(u_int32_t) ); *((u_int32_t*)(*Param)->SetValue) = 0; (*Param)->SetLen = sizeof(u_int32_t); break; case dtSigned32 : // Create Value pointer (*Param)->Value = (int32_t*)malloc( sizeof(int32_t) ); *((int32_t*)(*Param)->Value) = 0; (*Param)->Len = sizeof(int32_t); // Create Set Value pointer (*Param)->SetValue = (int32_t*)malloc( sizeof(int32_t) ); *((int32_t*)(*Param)->SetValue) = 0; (*Param)->SetLen = sizeof(int32_t); break; case dtFloat32 : // Create Value pointer (*Param)->Value = (float*)malloc( sizeof(float) ); *((float*)(*Param)->Value) = 0.0; (*Param)->Len = sizeof(float); // Create Set Value pointer (*Param)->SetValue = (float*)malloc( sizeof(float) ); *((float*)(*Param)->SetValue) = 0.0; (*Param)->SetLen = sizeof(float); break; case dtString : // Create Value pointer (*Param)->Len = ParamLen; (*Param)->Value = (char*)malloc( ParamLen + 1 ); memset( (*Param)->Value, 0x0, (*Param)->Len+1 ); // Create Set Value pointer (*Param)->SetLen = ParamLen; (*Param)->SetValue = (char*)malloc( ParamLen + 1 ); memset( (*Param)->SetValue, 0x0, (*Param)->SetLen+1 ); break; } } // Mark as updated (*Param)->Changed = true; // Report creation Log->Message( DebugLevel, dlLow, "%s: Param added - '%s' (%s)", Name, ParamName, DataTypeName[DataType] ); return *Param; } //--------------------------------------------------------------------------- bool CDeviceCore::DestroyDeviceParam( TDevice * Device, const char * ParamName ) { TDeviceParam ** Param = NULL; // Find param if (!(Param = GetDeviceParamPtr( Device, ParamName ))) return false; // Destroy Param if (!DestroyDeviceParam( Param )) return false; // Parameter destroyed return true; } //--------------------------------------------------------------------------- bool CDeviceCore::DestroyDeviceParam( TDeviceParam ** Param ) { TDeviceParam * NextParam = NULL; // Validate param if (!Param || !*Param) return false; // Save reference to next Param NextParam = (*Param)->Next; // Destroy Param structure if ((*Param)->Name) free( (*Param)->Name ); if ((*Param)->Value) free( (*Param)->Value ); if ((*Param)->SetValue) free( (*Param)->SetValue ); // Destroy Param free( *Param ); // Remove from list *Param = NextParam; // Parameter successfully destroyed return true; } //--------------------------------------------------------------------------- bool CDeviceCore::UpdateUnsignedValue( TDeviceParam * Param, const u_int32_t Value, bool Init ) { bool Changed = false; // Validate if (!Param) return false; switch (Param->DataType) { case dtUnsigned16 : if (Init || (*((u_int16_t*)Param->Value) != Value)) { // Set new value & mark change *((u_int16_t*)Param->Value) = Value; // Mark change & log event Changed = true; Log->Message( DebugLevel, dlLow, "%s: '%s' %s - %u", Name, Param->Name, ((Init)? "initialised" : "changed"), *((u_int16_t*)Param->Value) ); } break; case dtUnsigned32 : if (Init || (*((u_int32_t*)Param->Value) != Value)) { // Set new value & mark change *((u_int32_t*)Param->Value) = Value; // Mark change & log event Changed = true; Log->Message( DebugLevel, dlLow, "%s: '%s' %s - %u", Name, Param->Name, ((Init)? "initialised" : "changed"), *((u_int32_t*)Param->Value) ); } break; case dtFloat32 : // Special case, where float value is stored in Integer memory (e.g. modbus register) if (Init || (*((u_int32_t*)Param->Value) != Value)) { // Set new value & mark change *((u_int32_t*)Param->Value) = Value; // Mark change & log event Changed = true; Log->Message( DebugLevel, dlLow, "%s: '%s' %s - %f", Name, Param->Name, ((Init)? "initialised" : "changed"), *((float*)Param->Value) ); } break; default : // Invalid Data Type return false; } // Generate Channel Event if (Changed) { Param->Changed = true; EventOutput( Param, Changed ); } return Changed; } //--------------------------------------------------------------------------- bool CDeviceCore::UpdateSignedValue( TDeviceParam * Param, const int32_t Value, bool Init ) { bool Changed = false; // Validate if (!Param) return false; switch (Param->DataType) { case dtSigned16 : if (Init || (*((int16_t*)Param->Value) != Value)) { // Set new value & mark change *((int16_t*)Param->Value) = Value; // Mark change & log event Changed = true; Log->Message( DebugLevel, dlLow, "%s: '%s' %s - %d", Name, Param->Name, ((Init)? "initialised" : "changed"), *((int16_t*)Param->Value) ); } break; case dtSigned32 : if (Init || (*((int32_t*)Param->Value) != Value)) { // Set new value & mark change *((int32_t*)Param->Value) = Value; // Mark change & log event Changed = true; Log->Message( DebugLevel, dlLow, "%s: '%s' %s - %d", Name, Param->Name, ((Init)? "initialised" : "changed"), *((int32_t*)Param->Value) ); } break; default : // Invalid Data Type return false; } // Generate Channel Event if (Changed) { Param->Changed = true; EventOutput( Param, Changed ); } return Changed; } //--------------------------------------------------------------------------- bool CDeviceCore::UpdateFloatValue( TDeviceParam * Param, const float Value, bool Init ) { bool Changed = false; // Validate if (!Param) return false; switch (Param->DataType) { case dtFloat32 : if (Init || (*((float*)Param->Value) != Value)) { // Set new value & mark change *((float*)Param->Value) = Value; // Mark change & log event Changed = true; Log->Message( DebugLevel, dlLow, "%s: '%s' %s - %f", Name, Param->Name, ((Init)? "initialised" : "changed"), *((float*)Param->Value) ); } break; default : // Invalid Data Type return false; } // Generate Channel Event if (Changed) { Param->Changed = true; EventOutput( Param, Changed ); } return Changed; } //--------------------------------------------------------------------------- bool CDeviceCore::UpdateStringValue( TDeviceParam * Param, const char * Value, const int Len, bool Init ) { bool Changed = false; // Validate if (!Param || (Param->DataType != dtString)) return false; switch (Param->DataType) { case dtString : // Update register if (Init || !CompareParamString( (char*)Param->Value, Param->Len, Value, Len)) { if (Len >= Param->Len) { // Copy full register length (ignore additional bytes) memcpy( Param->Value, Value, Param->Len ); ((char*)Param->Value)[ Param->Len ] = 0; // null terminate } else { // Copy new value memcpy( Param->Value, Value, Len ); // Zero pad remaining memset( &((char*)Param->Value)[Len], 0x0, (Param->Len - Len + 1) ); // Add null teriminate } // Mark Change Changed = true; Log->Message( DebugLevel, dlLow, "%s: '%s' %s - %s", Name, Param->Name, ((Init)? "initialised" : "changed"), (char*)Param->Value ); } break; default : // Invalid Data Type return false; } // Generate Channel Event if (Changed) { Param->Changed = true; EventOutput( Param, Changed ); } return Changed; } //--------------------------------------------------------------------------- bool CDeviceCore::SetUnsignedValue( TDeviceParam * Param, const u_int32_t Value, bool Force ) { // Validate if (!Param) return false; switch (Param->DataType) { case dtUnsigned16 : if (Force || (*((u_int16_t*)Param->SetValue) != Value)) { // Set new value *((u_int16_t*)Param->SetValue) = Value; // Mark change Param->SetChanged = true; } break; case dtUnsigned32 : if (Force || (*((u_int32_t*)Param->SetValue) != Value)) { // Set new value *((u_int32_t*)Param->SetValue) = Value; // Mark change Param->SetChanged = true; } break; default : // Invalid data return false; } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::SetSignedValue( TDeviceParam * Param, const int32_t Value, bool Force ) { // Validate if (!Param) return false; switch (Param->DataType) { case dtSigned16 : if (Force || (*((int16_t*)Param->SetValue) != Value)) { // Set new value *((int16_t*)Param->SetValue) = Value; // Mark change Param->SetChanged = true; } break; case dtSigned32 : if (Force || (*((int32_t*)Param->SetValue) != Value)) { // Set new value *((int32_t*)Param->SetValue) = Value; // Mark change Param->SetChanged = true; } break; default : // Invalid data return false; } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::SetFloatValue( TDeviceParam * Param, const float Value, bool Force ) { // Validate if (!Param) return false; switch (Param->DataType) { case dtFloat32 : if (Force || (*((float*)Param->SetValue) != Value)) { // Set new value *((float*)Param->SetValue) = Value; // Mark change Param->SetChanged = true; } break; default : // Invalid data return false; } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::SetStringValue( TDeviceParam * Param, const char * Value, const int Len, bool Force ) { // Validate if (!Param || (Param->DataType != dtString) || (Len > Param->Len)) return false; // Update register if (Force || !CompareParamString( (char*)Param->Value, Param->Len, Value, Len)) { if (Len >= Param->SetLen) { // Copy full register length (ignore additional bytes) memcpy( Param->SetValue, Value, Param->SetLen ); ((char*)Param->SetValue)[ Param->SetLen ] = 0; // null terminate } else { // Copy new value memcpy( Param->SetValue, Value, Len ); // Zero pad remaining memset( &((char*)Param->SetValue)[Len], 0x0, (Param->SetLen - Len + 1) ); // null terminate } // Mark Change Param->SetChanged = true; } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::SetValue( TDeviceParam * Param, const char * Value, const int Len, bool Force ) { u_int32_t UnsignedVal; int32_t SignedVal; float FloatVal; char * TempStr; bool UseTempStr = false; // Validate if (!Param || !Value || !Len) return false; // Check if string if (Param->DataType == dtString) { // Simply set string SetStringValue( Param, Value, Len, Force ); } else { // Ensure string is zero terminated if (Value[Len] != 0) { TempStr = (char *)malloc( Len+1 ); memcpy( TempStr, Value, Len ); TempStr[Len] = 0; UseTempStr = true; } // Convert to correct type switch (Param->DataType) { case dtUnsigned16 : case dtUnsigned32 : UnsignedVal = (u_int32_t)strtoul( ((UseTempStr)? TempStr : Value), NULL, 10 ); SetUnsignedValue( Param, UnsignedVal, Force ); break; case dtSigned16 : case dtSigned32 : SignedVal = (u_int32_t)strtol( ((UseTempStr)? TempStr : Value), NULL, 10 ); SetUnsignedValue( Param, SignedVal, Force ); break; case dtFloat32 : FloatVal = (u_int32_t) strtof( ((UseTempStr)? TempStr : Value), NULL ); SetUnsignedValue( Param, FloatVal, Force ); break; default : return false; } // Clear memory if (UseTempStr) { free( TempStr ); } } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::GetValue( TDeviceParam * Param, char * Value, int &Len ) { // Validate if (!Param || !Value) return false; // Check if return value longer than actual value switch (Param->DataType) { case dtUnsigned16 : sprintf( Value, "%u", (*((u_int16_t*)Param->Value)) ); break; case dtUnsigned32 : sprintf( Value, "%u", (*((u_int32_t*)Param->Value)) ); break; case dtSigned16 : sprintf( Value, "%d", (*((int16_t*)Param->Value)) ); break; case dtSigned32 : sprintf( Value, "%d", (*((int32_t*)Param->Value)) ); break; case dtFloat32 : sprintf( Value, "%f", (*((float*)Param->Value)) ); break; case dtString : if (Len < Param->Len) { // Only copy requested no of chars memcpy( Value, Param->Value, Len ); Value[ Len ] = 0; } else { memcpy( Value, Param->Value, Param->Len ); Value[ Param->Len ] = 0; } break; } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::CompareParamString( const char * ParamValue, const int ParamLen, const char * Value, const int Len ) { bool Match = true; char * Pos = NULL; // Compare Value against current value if (Len >= ParamLen) { // Compare full register length (ignore additional bytes) if (memcmp( Value, ParamValue, ParamLen)) { Match = false; } } else { // Compare length of new value only if (memcmp( ParamValue, Value, Len)) { Match = false; } // Remaining bytes must be null else { for (Pos = (char*)ParamValue; Pos < (char*)ParamValue + ParamLen; Pos++) { if (*Pos != 0) { Match = false; break; } } } } // Return result return Match; } //--------------------------------------------------------------------------- bool CDeviceCore::SetParamScan( TDeviceParam * Param, bool Scan, const char * ChannelName, long pUpdateInterval ) { // Validate if (!Param) return false; // Set scan parameters Param->Scan = Scan; Param->EventChannel = GetChannel( ChannelName ); // Configure Update timer if (pUpdateInterval) { Param->UpdateInterval = pUpdateInterval; SetStartTime( &(Param->UpdateTimeout) ); } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::TimedParamEvents() { TDevice * Device = NULL; TDeviceParam * Param = NULL; // Loop through all devices Device = FirstDevice; while (Device) { // Loop through all scan parameters while ((Param = GetNextScanParam( Device, Param ))) { // Generate timed events EventOutput( Param, false ); } Device = Device->Next; } return true; } //--------------------------------------------------------------------------- // Generate Event output bool CDeviceCore::EventOutput( TDeviceParam * Param, bool Force ) { // Validate if (!Param || !(Param->EventChannel)) return false; // Check Timer or force if (Force || (Param->UpdateInterval && Timeout( Param->UpdateTimeout, Param->UpdateInterval )) ) { // Post event char Message[200]; switch (Param->DataType) { case dtUnsigned16 : sprintf( Message, "%s: %u\n", Param->Name, *((u_int16_t*)Param->Value) ); break; case dtUnsigned32 : sprintf( Message, "%s: %u\n", Param->Name, *((u_int32_t*)Param->Value) ); break; case dtSigned16 : sprintf( Message, "%s: %d\n", Param->Name, *((int16_t*)Param->Value) ); break; case dtSigned32 : sprintf( Message, "%s: %d\n", Param->Name, *((int32_t*)Param->Value) ); break; case dtFloat32 : sprintf( Message, "%s: %f\n", Param->Name, *((float*)Param->Value) ); break; case dtString : sprintf( Message, "%s: %s\n", Param->Name, (char*)Param->Value ); break; } Output( Param->EventChannel, Message, strlen(Message) ); // Reset timer if (Param->UpdateInterval) { SetStartTime( &(Param->UpdateTimeout) ); } } return true; } //---------------------------------------------------------------------------