/* * DeviceCore.cpp * * Created on: 18 May 2016 * Author: wentzelc */ //--------------------------------------------------------------------------- // Standard C/C++ Libraries /* none */ // redA Libraries #include "ApplicationCore.h" #include "DeviceCore.h" //--------------------------------------------------------------------------- // Global Vars extern char * ProcessName; extern CApplication * Application; //--------------------------------------------------------------------------- //CFunctionCore * NewDeviceCore( const char * Name ) { // return (CFunctionCore*) new CDeviceCore( Name ); //} //--------------------------------------------------------------------------- CDeviceCore::CDeviceCore( const char * pName, const char * pType ) : CFunctionCore( pName, pType ) { // Polling PollStep = 0; PollInterval = 250; SetStartTime( &PollWait ); WaitingForReply = false; ReplyTimeout = 200; PollRetry = 0; MaxRetries = 3; // Data Structures DeviceInit = true; ConfigTypes = new CDataMember(); JSONparse = new CJSONparse( ConfigTypes ); } //--------------------------------------------------------------------------- CDeviceCore::~CDeviceCore() { // Destroy Params while (FirstDevice) DestroyDevice( &FirstDevice ); while (FirstDeviceType) DestroyDevice( &FirstDeviceType ); if (JSONparse) delete JSONparse; if (ConfigTypes) delete ConfigTypes; } //--------------------------------------------------------------------------- bool CDeviceCore::Init( CDataMember * FunctionConfig ) { char * ConfigName = NULL; CDataMember * PollConfig = NULL; int IntVal1; int IntVal2; // Call Previous load config if (!CFunctionCore::Init( FunctionConfig )) return false; // Add Channels if (!(CmdChannel = GetChannel( "Command" ))) CmdChannel = AddChannel( "Command", true, true ); if (!(DeviceChannel = GetChannel( "Device" ))) DeviceChannel = AddChannel( "Device", true, true ); if (!(EventChannel = GetChannel( "Event" ))) EventChannel = AddChannel( "Event", true, true ); // Get configuration if ((Config = FunctionConfig->GetChild( "Config", true )) && Config->isString()) { ConfigName = (char*)FunctionConfig->GetChStr( "Config" ); Config = Application->Config->GetChild( ConfigName, true ); } if (Config->isNull()) Config->SetObject(); // Load Polling configuration PollConfig = Config->GetChild( "Polling", true ); if (PollConfig->isNull()) PollConfig->SetObject(); IntVal1 = PollConfig->GetChInt( "PollInterval", 500, true ); SetPollParam( IntVal1 ); IntVal1 = PollConfig->GetChInt( "ReplyTimeout", 250, true ); IntVal2 = PollConfig->GetChInt( "MaxRetries", 3, true ); SetReplyParam( IntVal1, IntVal2 ); Log->Message( LogLevel, dlMedium, "%s/%s: Set polling param - Int:%d, T/O:%d, Max:%d", ProcessName, Name, PollInterval, ReplyTimeout, MaxRetries ); // Update Devices -- may want to do it from derived class intead if (DeviceInit) { CDeviceCore::InitDevices( FunctionConfig ); } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::InitDevices( CDataMember * FunctionConfig ) { CDataMember * DeviceData = NULL; TDevice * Device = NULL; char * DeviceName = NULL; char * DeviceType = NULL; // Load Device Types DeviceData = Config->GetChFirstChild( "DeviceTypes", true ); while (DeviceData) { DeviceName = (char*)DeviceData->GetName(); Device = AddDeviceType( DeviceName ); if (DeviceData->isString()) { if (JSONparse->ReadFromFile( DeviceName, DeviceData->GetStr() )) { // Contains file reference InitDeviceParams( Device, ConfigTypes->GetChild( DeviceName ) ); InitParamGroups( Device, ConfigTypes->GetChild( DeviceName ) ); JSONparse->WriteToFile( DeviceName, DeviceData->GetStr() ); } else { if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Fail to load device type '%s' from file: '%s'", ProcessName, Name, DeviceName, DeviceData->GetStr() ); } } else if (DeviceData->isObject()) { InitDeviceParams( Device, DeviceData ); // Contains definition InitParamGroups( Device, DeviceData ); } else { if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Invalid device type config for '%s'", ProcessName, Name, DeviceName ); } DeviceData = DeviceData->GetNextPeer(); } // Load Actual Devices DeviceData = Config->GetChFirstChild( "Devices", true ); while (DeviceData) { DeviceName = (char*)DeviceData->GetName(); DeviceType = (char*)DeviceData->GetChStr( "Type" ); Device = AddDevice( DeviceName, DeviceType ); InitDeviceParams( Device, DeviceData ); InitParamGroups( Device, DeviceData ); DeviceData = DeviceData->GetNextPeer(); } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::InitDeviceParams( TDevice * Device, CDataMember * DeviceConfig ) { CDataMember * ParamData; CDataMember * InitVal; TDeviceParam * Param; TDeviceParam * Template; EDeviceDataType DataType; bool Read; bool Write; char * EventOut; int EventInt; // Copy Template Template = (Device->Template)? Device->Template->FirstParam : NULL; while (Template) { if ((Param = AddDeviceParam( Device, Template->Name, Template->DataType, Template->Len ))) { SetParamAccess( Param, Template->Read, Template->Write ); SetParamEvent( Param, (Template->EventChannel)? Template->EventChannel->Name : NULL, Template->EventInterval ); if ((Template->DataType == dtUnsigned16) || (Template->DataType == dtUnsigned32)) { UpdateUnsignedValue( Param, *((u_int16_t*)Template->Value), Template->Changed ); if (Template->SetChanged) SetUnsignedValue( Param, *((u_int16_t*)Template->Value), true ); } else if ((Template->DataType == dtSigned16) || (Template->DataType == dtSigned32)) { UpdateSignedValue( Param, *((int16_t*)Template->Value), Template->Changed ); if (Template->SetChanged) SetSignedValue( Param, *((int16_t*)Template->Value), true ); } else if (Template->DataType == dtFloat32) { UpdateFloatValue( Param, *((float*)Template->Value), Template->Changed ); if (Template->SetChanged) SetFloatValue( Param, *((float*)Template->Value), true ); } else if (Template->DataType == dtString) { UpdateStringValue( Param, (char*)Template->Value, Template->Len, Template->Changed ); if (Template->SetChanged) SetStringValue( Param, (char*)Template->Value, Template->Len, true ); } } Template = Template->Next; } // Add Parameters ParamData = DeviceConfig->GetChFirstChild( "Parameters", true ); while (ParamData) { DataType = GetDataType((char*)ParamData->GetChStr( "Type", "Unsigned16", true )); if ((Param = AddDeviceParam( Device, ParamData->GetName(), DataType, 1 ))) { Read = ParamData->GetChBool( "Read", false, true ); Write = ParamData->GetChBool( "Write", false, true ); SetParamAccess( Param, Read, Write ); EventOut = (char*)ParamData->GetChStr( "EventChannel", NULL, false ); EventInt = ParamData->GetChInt( "EventInterval", 0, false ); SetParamEvent( Param, EventOut, EventInt ); if ((InitVal = ParamData->GetChild( "InitValue", false ))) { if ((DataType == dtUnsigned16) || (DataType == dtUnsigned32)) { UpdateUnsignedValue( Param, InitVal->GetInt(0), Read ); if (Write) SetUnsignedValue( Param, InitVal->GetInt(0), true ); } else if ((DataType == dtSigned16) || (DataType == dtSigned32)) { UpdateSignedValue( Param, InitVal->GetInt(0), Read ); if (Write) SetSignedValue( Param, InitVal->GetInt(0), true ); } else if (DataType == dtFloat32) { UpdateFloatValue( Param, InitVal->GetFloat(0), Read ); if (Write) SetFloatValue( Param, InitVal->GetFloat(0), true ); } else if (DataType == dtString) { UpdateStringValue( Param, InitVal->GetStr(""), InitVal->GetLen(), Read ); if (Write) SetStringValue( Param, InitVal->GetStr(""), InitVal->GetLen(), true ); } } } // Read + Event Out ParamData = ParamData->GetNextPeer(); } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::InitParamGroups( TDevice * Device, CDataMember * DeviceConfig ) { CDataMember * GroupData; CDataMember * ItemData; TDeviceParamGroup * Group; TDeviceParamGroup * GroupTemplate; TDeviceParamItem * ItemTemplate; // Copy Template GroupTemplate = (Device->Template)? Device->Template->FirstParamGroup : NULL; while (GroupTemplate) { if ((Group = AddParamGroup( Device, GroupTemplate->Name ))) { ItemTemplate = GroupTemplate->FirstParam; while (ItemTemplate) { AddParamItem( Group, ItemTemplate->Param->Name ); ItemTemplate = ItemTemplate->NextItem; } } GroupTemplate = GroupTemplate->NextGroup; } // Add Parameters GroupData = DeviceConfig->GetChFirstChild( "ParamGroups", true ); while (GroupData) { if ((Group = AddParamGroup( Device, GroupData->GetName() ))) { ItemData = GroupData->GetElement( 0 ); while (ItemData) { AddParamItem( Group, ItemData->GetStr() ); ItemData = ItemData->GetNextPeer(); } } GroupData = GroupData->GetNextPeer(); } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::SetPollParam( int pPollInterval ) { PollInterval = pPollInterval; return true; } //--------------------------------------------------------------------------- bool CDeviceCore::SetReplyParam( int pReplyTimeout, int pMaxRetries ) { ReplyTimeout = pReplyTimeout; MaxRetries = pMaxRetries; 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; if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Device '%s' %s", ProcessName, 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 if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Device '%s' timeout, retry [%d]", ProcessName, Name, DeviceChannel->Name, ActiveDevice->Name, PollRetry ); } else { // Log Event if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Device '%s' timeout, max [%d]", ProcessName, 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::AddDeviceType( const char * DeviceTypeName ) { TDevice ** DeviceType = GetDeviceTypePtr( DeviceTypeName ); if (!DeviceType) { if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Could not add device type '%s'", ProcessName, Name, ((DeviceTypeName)? DeviceTypeName : "[device type]") ); return NULL; } else if (*DeviceType) { if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Device type '%s' already exists", ProcessName, Name, DeviceTypeName ); } else { *DeviceType = new TDevice; (*DeviceType)->Type = (char *)malloc( strlen( DeviceTypeName )+1 ); strcpy( (*DeviceType)->Type, DeviceTypeName ); if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Added device type '%s'", ProcessName, Name, DeviceTypeName ); } return *DeviceType; } //--------------------------------------------------------------------------- TDevice * CDeviceCore::AddDevice( const char * DeviceName, const char * Type ) { TDevice ** Device = GetDevicePtr( DeviceName ); if (!Device) { if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Could not add device '%s' (%s)", ProcessName, Name, ((DeviceName)? DeviceName : "[device]"), ((Type)? Type : "no type") ); return NULL; } else if (*Device) { if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Device type '%s' (%s) already exists", ProcessName, Name, DeviceName, (((*Device)->Type)? (*Device)->Type : "no type") ); } else { *Device = new TDevice; (*Device)->Name = (char *)malloc( strlen( DeviceName )+1 ); strcpy( (*Device)->Name, DeviceName ); if (Type && *Type) { (*Device)->Type = (char *)malloc( strlen( Type )+1 ); strcpy( (*Device)->Type, Type ); (*Device)->Template = GetDeviceType( Type ); } if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Added device '%s' (%s%s)", ProcessName, Name, DeviceName, ((Type)? Type : "no type"), (((*Device)->Template)? "*" : "") ); } return *Device; } //--------------------------------------------------------------------------- bool CDeviceCore::DestroyDevice( TDevice ** Device ) { TDevice * NextDevice = NULL; // Validate Device if (!Device || !*Device) return false; // Save reference to next Device NextDevice = (*Device)->Next; // Destroy Device structure if ((*Device)->Name) free( (*Device)->Name ); if ((*Device)->Type) free( (*Device)->Type ); // Destroy parameters & groups while ((*Device)->FirstParam) { DestroyDeviceParam( &((*Device)->FirstParam) ); } while ((*Device)->FirstParamGroup) { DestroyParamGroup( &((*Device)->FirstParamGroup) ); } // Destroy Device delete *Device; // Remove from list *Device = NextDevice; // Device successfully destroyed return true; } //--------------------------------------------------------------------------- TDeviceParam * CDeviceCore::AddDeviceParam( TDevice * Device, const char * ParamName, EDeviceDataType DataType, int ParamLen ) { TDeviceParam ** Param = GetDeviceParamPtr( Device, ParamName ); if (!Param) { // Invalid device/param if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Param '%s/%s' cannot be created", ProcessName, Name, ((!Device)? "[device]" : (Device->Name)? Device->Name : Device->Type), ((!ParamName)? "[param]" : ParamName)); return NULL; } else if (*Param) { // Parameter already exists if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Param '%s/%s' already exists", ProcessName, Name, ((Device->Name)? Device->Name : Device->Type), ParamName, DataTypeName[DataType]); } else { // Create register *Param = new TDeviceParam; // Set Name (*Param)->Name = (char *)malloc( strlen( ParamName )+1 ); strcpy( (*Param)->Name, ParamName ); (*Param)->DataType = DataType; (*Param)->Device = Device; // 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; default : break; } (*Param)->Changed = true; if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Added param '%s/%s' (%s)", ProcessName, Name, ((Device->Name)? Device->Name : Device->Type), ParamName, DataTypeName[DataType]); } return *Param; } //--------------------------------------------------------------------------- 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 delete *Param; // Remove from list *Param = NextParam; // Parameter successfully destroyed return true; } //--------------------------------------------------------------------------- TDeviceParamGroup * CDeviceCore::AddParamGroup( TDevice * Device, const char * GroupName ) { TDeviceParamGroup ** ParamGroup = GetParamGroupPtr( Device, GroupName ); if (!ParamGroup) { if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Could not add param group '%s'", ProcessName, Name, ((!Device)? "[device]" : (Device->Name)? Device->Name : Device->Type), ((GroupName)? GroupName : "[group]") ); return NULL; } else if (*ParamGroup) { if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Param group '%s/%s' already exists", ProcessName, Name, ((Device->Name)? Device->Name : Device->Type), GroupName ); } else { *ParamGroup = new TDeviceParamGroup; (*ParamGroup)->Name = (char *)malloc( strlen( GroupName )+1 ); strcpy( (*ParamGroup)->Name, GroupName ); (*ParamGroup)->Device = Device; if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Added param group '%s/%s'", ProcessName, Name, ((Device->Name)? Device->Name : Device->Type), GroupName ); } return *ParamGroup; } //--------------------------------------------------------------------------- bool CDeviceCore::DestroyParamGroup( TDeviceParamGroup ** ParamGroup ) { TDeviceParamGroup * NextGroup = NULL; // Validate ParamGroup if (!ParamGroup || !*ParamGroup) return false; // Save reference to next Param Group NextGroup = (*ParamGroup)->NextGroup; // Destroy Param Group structure if ((*ParamGroup)->Name) free( (*ParamGroup)->Name ); // Destroy items while ((*ParamGroup)->FirstParam) { DestroyParamItem( &((*ParamGroup)->FirstParam) ); } // Destroy ParamGroup delete *ParamGroup; // Remove from list *ParamGroup = NextGroup; // ParamGroup successfully destroyed return true; } //--------------------------------------------------------------------------- TDeviceParamItem * CDeviceCore::AddParamItem( TDeviceParamGroup * Group, const char * ParamName ) { TDeviceParamItem ** Item = GetParamItemPtr( Group, ParamName ); TDeviceParam * Param = NULL; if (!Item) { // Invalid group/param if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Param group item '%s/%s' cannot be created", ProcessName, Name, ((!Group)? "[group]" : Group->Name), ((!ParamName)? "[param]" : ParamName)); return NULL; } else if (*Item) { // Item already exists if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Param Group item '%s/%s' already exists", ProcessName, Name, Group->Name, ParamName ); } else if (!(Param = GetDeviceParam( Group->Device, ParamName ))) { // Parameter does not exist if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Param '%s/%s' does not exists - Cannot add to Param group '%s'", ProcessName, Name, ((Group->Device->Name)? Group->Device->Name : Group->Device->Type), ParamName, Group->Name ); } else { *Item = new TDeviceParamItem; (*Item)->Param = Param; if (Param->Read) Group->Read = true; if (Param->Write) Group->Write = true; if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Added param group item '%s/%s/%s'", ProcessName, Name, ((Group->Device->Name)? Group->Device->Name : Group->Device->Type), Group->Name, ParamName ); } return *Item; } //--------------------------------------------------------------------------- bool CDeviceCore::DestroyParamItem( TDeviceParamItem ** Item ) { TDeviceParamItem * NextParam = NULL; if (!Item || !*Item) return false; NextParam = (*Item)->NextItem; delete *Item; *Item = NextParam; return true; } //--------------------------------------------------------------------------- bool CDeviceCore::SetParamAccess( TDeviceParam * Param, bool Read, bool Write ) { if (!Param) return false; Param->Read = Read; Param->Write = Write; if (Log) { char * DeviceName = (Param->Device->Name)? Param->Device->Name : Param->Device->Type; Log->Message( LogLevel, dlMedium, "%s/%s: Set param '%s/%s' access - R:%d, W:%d", ProcessName, Name, DeviceName, Param->Name, Read, Write ); } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::SetParamEvent( TDeviceParam * Param, const char * ChannelName, long pEventInterval ) { if (!Param) return false; Param->EventChannel = GetChannel( ChannelName ); if (pEventInterval) { Param->EventInterval = pEventInterval; SetStartTime( &(Param->EventTimeout) ); } if (Log) { char * DeviceName = (Param->Device->Name)? Param->Device->Name : Param->Device->Type; Log->Message( LogLevel, dlMedium, "%s/%s: Set param '%s/%s' event - Ch:'%s', Int:%d", ProcessName, Name, DeviceName, Param->Name, ((ChannelName)? ChannelName : ""), pEventInterval ); } 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; if (Log) { char * DeviceName = (Param->Device->Name)? Param->Device->Name : Param->Device->Type; Log->Message( LogLevel, dlLow, "%s/%s: %s param '%s/%s' - %u", ProcessName, Name, ((Init)? "Init" : "Changed"), DeviceName, Param->Name, *((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; if (Log) { char * DeviceName = (Param->Device->Name)? Param->Device->Name : Param->Device->Type; Log->Message( LogLevel, dlLow, "%s/%s: %s param '%s/%s' - %u", ProcessName, Name, ((Init)? "Init" : "Changed"), DeviceName, Param->Name, *((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; if (Log) { char * DeviceName = (Param->Device->Name)? Param->Device->Name : Param->Device->Type; Log->Message( LogLevel, dlLow, "%s/%s: %s param '%s/%s' - %f", ProcessName, Name, ((Init)? "Init" : "Changed"), DeviceName, Param->Name, *((float*)Param->Value) ); } } break; default : // Invalid Data Type return false; } // Generate Channel Event if (Changed) { Param->Changed = true; if (Param->Device->Name) EventOutput( Param, Changed ); // Only send event of actual device, not type/template } 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; if (Log) { char * DeviceName = (Param->Device->Name)? Param->Device->Name : Param->Device->Type; Log->Message( LogLevel, dlLow, "%s/%s: %s param '%s/%s' - %d", ProcessName, Name, ((Init)? "Init" : "Changed"), DeviceName, Param->Name, *((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; if (Log) { char * DeviceName = (Param->Device->Name)? Param->Device->Name : Param->Device->Type; Log->Message( LogLevel, dlLow, "%s/%s: %s param '%s/%s' - %d", ProcessName, Name, Param->Name, ((Init)? "Init" : "Changed"), DeviceName, Param->Name, *((int32_t*)Param->Value) ); } } break; default : // Invalid Data Type return false; } // Generate Channel Event if (Changed) { Param->Changed = true; if (Param->Device->Name) EventOutput( Param, Changed ); // Only send event of actual device, not type/template } 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; if (Log) { char * DeviceName = (Param->Device->Name)? Param->Device->Name : Param->Device->Type; Log->Message( LogLevel, dlLow, "%s/%s: %s param '%s/%s' - %f", ProcessName, Name, ((Init)? "Init" : "Changed"), DeviceName, Param->Name, *((float*)Param->Value) ); } } break; default : // Invalid Data Type return false; } // Generate Channel Event if (Changed) { Param->Changed = true; if (Param->Device->Name) EventOutput( Param, Changed ); // Only send event of actual device, not type/template } 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; if (Log) { char * DeviceName = (Param->Device->Name)? Param->Device->Name : Param->Device->Type; Log->Message( LogLevel, dlLow, "%s/%s: %s param '%s/%s' - '%s'", ProcessName, Name, ((Init)? "Init" : "Changed"), DeviceName, Param->Name, (char*)Param->Value ); } } break; default : // Invalid Data Type return false; } // Generate Channel Event if (Changed) { Param->Changed = true; if (Param->Device->Name) EventOutput( Param, Changed ); // Only send event of actual device, not type/template } 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; default : 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::TimedParamEvents() { TDevice * Device = NULL; TDeviceParam * Param = NULL; // Loop through all devices Device = FirstDevice; while (Device) { // Loop through all Read parameters while ((Param = GetNextReadParam( Device, Param ))) { EventOutput( Param, false ); } Device = Device->Next; } return true; } //--------------------------------------------------------------------------- bool CDeviceCore::GetCmdParam( const char * Start, char * Param, char ** NextParam ) { char * NextDelimeter; int ParamLen; // Get length of param if ((NextDelimeter = strchr( (char*)Start, ',' ))) { ParamLen = NextDelimeter - Start; *NextParam = NextDelimeter + 1; } else { // remove \r\n if necessary ParamLen = strlen( Start ); if (Start[ParamLen-1] == '\n') ParamLen--; if (Start[ParamLen-1] == '\r') ParamLen--; *NextParam = NULL; } // Copy param strncpy( Param, Start, ParamLen ); Param[ParamLen] = 0; return true; } //--------------------------------------------------------------------------- int CDeviceCore::HandleCommand( const char *ChannelName, const char * Data, const int MaxLen ) { int Len; char ParamName[50]; char * NextParam = NULL; TDevice * Device = NULL; TDeviceParam * Param = NULL; char OutputStr[250]; // Show accepted input Log->Output( LogLevel, dlHigh, loNormal, Data, MaxLen, "%s/%s: Channel '%s' - IN:", ProcessName, Name, ChannelName ); // Get command command GetCmdParam( Data, ParamName, &NextParam ); if (!strcasecmp( "get", ParamName )) { // Check for additional parameters if (!NextParam) { // No Parameters Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - Missing 'get' parameters", ProcessName, Name, ChannelName ); return MaxLen; } // Get device name GetCmdParam( NextParam, ParamName, &NextParam ); if (!(Device = GetDevice( ParamName ))) { // Unknown Parameters Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - Unknown 'get' Device: '%s'", ProcessName, Name, ChannelName, ParamName ); return MaxLen; } // Get parameter name GetCmdParam( NextParam, ParamName, &NextParam ); if (!(Param = GetDeviceParam( Device, ParamName ))) { // Unknown Parameters Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - Unknown 'get' Param: '%s'", ProcessName, Name, ChannelName, ParamName ); return MaxLen; } // Check for additional parameters if (NextParam) { // Unused Parameters Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - Unnecessary 'get' parameters", ProcessName, Name, ChannelName ); return MaxLen; } // Build reply sprintf( OutputStr, "get,%s,", Param->Name ); Len = MaxLen - strlen(OutputStr); // Insert value if (!GetValue( (TDeviceParam*)Param, &OutputStr[ strlen(OutputStr) ], Len )) return false; // Send reply strcat( OutputStr, "\n" ); Output( ChannelName, OutputStr, strlen(OutputStr) ); return true; } else if (!strcasecmp( "set", ParamName )) { // Check for additional parameters if (!NextParam) { // No Parameters Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - Missing 'set' parameters", ProcessName, Name, ChannelName ); return MaxLen; } // Get Device name GetCmdParam( NextParam, ParamName, &NextParam ); if (!(Device = GetDevice( ParamName ))) { // Unknown Parameters Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - Unknown 'set' Device: '%s'", ProcessName, Name, ChannelName, ParamName ); return MaxLen; } // Get parameter name GetCmdParam( NextParam, ParamName, &NextParam ); if (!(Param = GetDeviceParam( Device, ParamName ))) { // Unknown Parameters Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - Unknown 'set' Param: '%s'", ProcessName, Name, ChannelName, ParamName ); return MaxLen; } // Get parameter value GetCmdParam( NextParam, ParamName, &NextParam ); if (strlen(ParamName) == 0) { // No Parameters Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - No 'set' value parameter", ProcessName, Name, ChannelName ); return MaxLen; } // Check for additional parameters if (NextParam) { // Unused Parameters Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - Unnecessary 'set' parameters", ProcessName, Name, ChannelName ); return MaxLen; } // Set value Len = MaxLen; if (!SetValue( Param, ParamName, Len, true )) return false; // Build & send reply sprintf( OutputStr, "set,%s,%s\n", Param->Name, ParamName ); Output( ChannelName, OutputStr ); return true; } else if (!strcasecmp( "status", ParamName )) { // Check for additional parameters if (!NextParam) { // No Parameters Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - Missing 'status' parameters", ProcessName, Name, ChannelName ); return MaxLen; } // Get Device name GetCmdParam( NextParam, ParamName, &NextParam ); if (!(Device = GetDevice( ParamName ))) { // Unknown Parameters Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - Unknown 'status' Device: '%s'", ProcessName, Name, ChannelName, ParamName ); return MaxLen; } // Check for additional parameters if (NextParam) { // Unused Parameters Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - Unnecessary 'status' parameters", ProcessName, Name, ChannelName ); return MaxLen; } // Build & send reply sprintf( OutputStr, "status,%s,%d\n", Device->Name, Device->Online ); Output( ChannelName, OutputStr ); return true; } else { // Unrecognised command Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' - Unrecognized command: '%s'", ProcessName, Name, ChannelName, ParamName ); return MaxLen; } return MaxLen; } //--------------------------------------------------------------------------- // Generate Event output bool CDeviceCore::EventOutput( TDeviceParam * Param, bool Force ) { // Validate if (!Param || !(Param->EventChannel)) return false; // Check Timer or force if (Force || (Param->EventInterval && Timeout( Param->EventTimeout, Param->EventInterval )) ) { // 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; default : break; } Output( Param->EventChannel, Message, strlen(Message) ); // Reset timer if (Param->EventInterval) { SetStartTime( &(Param->EventTimeout) ); } } return true; } //---------------------------------------------------------------------------