- Save reference to process config in Function Core
- Moved from DeviceCore to here as common feature
- "Config" : object
Allow additional config to be stored in application definition
- "Config" : string
Name reference to config object in main JSON file
- Remove from Device Core
1741 lines
59 KiB
C++
1741 lines
59 KiB
C++
/*
|
|
* 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
|
|
PollCycle = 0;
|
|
PollStep = 0;
|
|
PollInterval = 250;
|
|
SetStartTime( &PollWait );
|
|
|
|
WaitingForReply = false;
|
|
ReplyTimeout = 200;
|
|
InvalidReply = false;
|
|
|
|
PollRetry = 0;
|
|
MaxRetries = 3;
|
|
|
|
// Data Structures
|
|
DeviceInit = true;
|
|
ConfigTypes = new CDataMember();
|
|
ValueTree = new CDataMember();
|
|
JSONparse = new CJSONparse();
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CDeviceCore::~CDeviceCore()
|
|
{
|
|
// Destroy Params
|
|
while (FirstDevice)
|
|
DestroyDevice( &FirstDevice );
|
|
while (FirstDeviceType)
|
|
DestroyDevice( &FirstDeviceType );
|
|
if (JSONparse)
|
|
delete JSONparse;
|
|
if (ConfigTypes)
|
|
delete ConfigTypes;
|
|
if (ValueTree)
|
|
delete ValueTree;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDeviceCore::Init( CDataMember * FunctionConfig )
|
|
{
|
|
char * PersistFile = 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 );
|
|
|
|
// 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 );
|
|
|
|
// Load Value Tree from persistence file
|
|
if ((PersistFile = (char*)Config->GetChStr( "PersistFile", NULL, true ))) {
|
|
JSONparse->SetBase( ValueTree );
|
|
JSONparse->ReadFromFile( NULL, PersistFile );
|
|
|
|
Log->Message( LogLevel, dlMedium, "%s/%s: Persistence File '%s' loaded",
|
|
ProcessName, Name, PersistFile );
|
|
}
|
|
|
|
// Update Devices -- may want to do it from derived class intead
|
|
if (DeviceInit) {
|
|
InitDevices( FunctionConfig );
|
|
//JSONparse->SetBase( ValueTree );
|
|
//JSONparse->WriteToScreen( NULL, 2 );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDeviceCore::InitDevices( CDataMember * FunctionConfig )
|
|
{
|
|
CDataMember * DeviceConfig = NULL;
|
|
TDevice * Device = NULL;
|
|
char * Definition = NULL;
|
|
char * DeviceName = NULL;
|
|
int DeviceID = 0;
|
|
char * DeviceAddr = NULL;
|
|
char * DeviceType = NULL;
|
|
char * DataPath = NULL;
|
|
|
|
// Load Device Types
|
|
DeviceConfig = Config->GetChFirstChild( "DeviceTypes", true );
|
|
while (DeviceConfig)
|
|
{
|
|
DeviceType = (char*)DeviceConfig->GetName();
|
|
if (!DeviceConfig->isObject()) {
|
|
if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Invalid device type config for '%s'",
|
|
ProcessName, Name, DeviceType );
|
|
DeviceConfig = DeviceConfig->GetNextPeer();
|
|
continue;
|
|
}
|
|
|
|
DataPath = (char*)DeviceConfig->GetChStr( "DataPath", NULL );
|
|
Device = AddDeviceType( DeviceType, DataPath );
|
|
|
|
if (Device) {
|
|
if ((Definition = (char*)DeviceConfig->GetChStr( "Definition", NULL, false ))) {
|
|
JSONparse->SetBase( ConfigTypes );
|
|
if (JSONparse->ReadFromFile( DeviceType, Definition )) { // Contains file reference
|
|
InitDeviceParams( Device, ConfigTypes->GetChild( DeviceType ), NULL );
|
|
|
|
JSONparse->SetBase( ConfigTypes );
|
|
JSONparse->WriteToFile( DeviceType, Definition );
|
|
}
|
|
else {
|
|
if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Fail to load device type '%s' from file: '%s'",
|
|
ProcessName, Name, DeviceType, DeviceConfig->GetStr() );
|
|
}
|
|
}
|
|
else {
|
|
InitDeviceParams( Device, DeviceConfig, NULL ); // Contains definition
|
|
}
|
|
}
|
|
DeviceConfig = DeviceConfig->GetNextPeer();
|
|
}
|
|
|
|
// Load Actual Devices
|
|
DeviceConfig = Config->GetChFirstChild( "Devices", true );
|
|
while (DeviceConfig)
|
|
{
|
|
DeviceName = (char*)DeviceConfig->GetName();
|
|
if (!DeviceConfig->isObject()) {
|
|
if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Invalid device config for '%s'",
|
|
ProcessName, Name, DeviceName );
|
|
DeviceConfig = DeviceConfig->GetNextPeer();
|
|
continue;
|
|
}
|
|
|
|
DeviceID = DeviceConfig->GetChInt( "ID", 0 );
|
|
DeviceAddr = (char*)DeviceConfig->GetChStr( "Address", NULL );
|
|
DeviceType = (char*)DeviceConfig->GetChStr( "Type", NULL );
|
|
DataPath = (char*)DeviceConfig->GetChStr( "DataPath", NULL );
|
|
Device = AddDevice( DeviceName, DeviceType, DeviceID, DeviceAddr, DataPath );
|
|
|
|
if (Device) {
|
|
InitDeviceParams( Device, DeviceConfig, Device->DataNode );
|
|
}
|
|
DeviceConfig = DeviceConfig->GetNextPeer();
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDeviceCore::InitDeviceParams( TDevice * Device, CDataMember * DeviceConfig, CDataMember * ParentNode )
|
|
{
|
|
CDataMember * ParamConfig;
|
|
TDeviceParam * Template;
|
|
bool isType = (Device->Name)? false : true;
|
|
|
|
// Copy Template Params
|
|
Template = (Device->Template)? Device->Template->FirstParam : NULL;
|
|
while (Template) {
|
|
CopyTemplateParam( Device, Template, ParentNode );
|
|
Template = Template->Next;
|
|
}
|
|
CopyTemplateParamGroups( Device );
|
|
|
|
// Read Device Params
|
|
ParamConfig = DeviceConfig->GetChFirstChild( "Parameters", true );
|
|
while (ParamConfig) {
|
|
InitDeviceParam( Device, ParamConfig, NULL, ((isType)? NULL : Device->DataPath), ParentNode, NULL );
|
|
ParamConfig = ParamConfig->GetNextPeer();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDeviceCore::CopyTemplateParam( TDevice * Device, TDeviceParam * Template, CDataMember * ParentNode )
|
|
{
|
|
TDeviceParam * Param = NULL;
|
|
CDataMember * DataNode = NULL;
|
|
char * DataPath = NULL;
|
|
|
|
if ((Param = AddDeviceParam( Device, Template->Name, Template->DataType, Template->Len )))
|
|
{
|
|
SetParamAccess( Param, Template->Read, Template->Write );
|
|
|
|
if (Template->DataPath) {
|
|
DataPath = (char*)malloc( strlen(Device->DataPath) + strlen(Template->DataPath) + 2);
|
|
sprintf( DataPath, "%s/%s", Device->DataPath, Template->DataPath );
|
|
DataNode = (Device->DataNode)? Device->DataNode->GetChild( Template->DataPath, true ) : NULL;
|
|
SetDataPath( Param, DataPath, DataNode );
|
|
}
|
|
|
|
if ((Template->DataType == dtUnsigned16) || (Template->DataType == dtUnsigned32_HL) || (Template->DataType == dtUnsigned32_LH)) {
|
|
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_HL) || (Template->DataType == dtSigned32_LH)) {
|
|
UpdateSignedValue( Param, *((int16_t*)Template->Value), Template->Changed );
|
|
if (Template->SetChanged)
|
|
SetSignedValue( Param, *((int16_t*)Template->Value), true );
|
|
}
|
|
else if ((Template->DataType == dtFloat32_L) || (Template->DataType == dtFloat32_B)) {
|
|
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 );
|
|
}
|
|
|
|
SetParamEvent( Param, (Template->EventChannel)? Template->EventChannel->Name : NULL, Template->EventInterval );
|
|
}
|
|
|
|
if (DataPath) free( DataPath );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDeviceCore::CopyTemplateParamGroups( TDevice * Device )
|
|
{
|
|
TDeviceParamGroup * TemplateGroup;
|
|
TDeviceParamItem * TemplateItem;
|
|
TDeviceParamGroup * ParamGroup;
|
|
TDeviceParam * Param;
|
|
|
|
// Check if Template Groups exists
|
|
if (!Device->Template || !(TemplateGroup = Device->Template->FirstParamGroup))
|
|
return false;
|
|
|
|
while (TemplateGroup) {
|
|
// Create group
|
|
ParamGroup = AddParamGroup( Device, TemplateGroup->Name );
|
|
|
|
// Add Registers
|
|
TemplateItem = TemplateGroup->FirstParam;
|
|
while (TemplateItem) {
|
|
if ((Param = GetDeviceParam( Device, TemplateItem->Param->Name )))
|
|
AddParamItem( ParamGroup, Param );
|
|
TemplateItem = TemplateItem->NextItem;
|
|
}
|
|
TemplateGroup = TemplateGroup->NextGroup;
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDeviceCore::InitDeviceParam( TDevice * Device, CDataMember * ParamConfig, const char * ParentName,
|
|
const char * ParentPath, CDataMember * ParentNode, TDeviceParamGroup * ParentParamGroup )
|
|
{
|
|
char * NodeName;
|
|
char * FullName;
|
|
char * DataPath;
|
|
CDataMember * DataNode;
|
|
CDataMember * Children;
|
|
CDataMember * ChildConfig;
|
|
CDataMember * ChildMap;
|
|
|
|
CDataMember * InitVal;
|
|
TDeviceParam * Param;
|
|
|
|
EDeviceDataType DataType;
|
|
|
|
char * ParamGroupName;
|
|
SDeviceParamGroup * ParamGroup;
|
|
|
|
bool Read;
|
|
bool Write;
|
|
|
|
char * EventOut;
|
|
int EventInt;
|
|
|
|
// Create Node Name
|
|
NodeName = (char*)ParamConfig->GetName();
|
|
if (ParentName && *ParentName) {
|
|
FullName = (char*)malloc( strlen(ParentName) + strlen(NodeName) + 2 );
|
|
sprintf( FullName, "%s_%s", ParentName, NodeName );
|
|
} else {
|
|
FullName = strdup( NodeName );
|
|
}
|
|
|
|
// Create Node Path
|
|
DataNode = (ParentNode)? ParentNode->GetChild( NodeName, true ) : NULL;
|
|
if (ParentPath && *ParentPath) {
|
|
DataPath = (char*)malloc( strlen(ParentPath) + strlen(NodeName) + 2 );
|
|
sprintf( DataPath, "%s/%s", ParentPath, NodeName );
|
|
} else {
|
|
DataPath = strdup( NodeName );
|
|
}
|
|
|
|
if ((Children = ParamConfig->GetChild( "Children", false ))) {
|
|
// Data Branch
|
|
if ((ParamGroupName = (char*)ParamConfig->GetChStr( "ParamGroup", NULL, false ))) {
|
|
ParamGroup = AddParamGroup( Device, ParamGroupName );
|
|
} else {
|
|
ParamGroup = ParentParamGroup;
|
|
}
|
|
|
|
if (Children->isString()) {
|
|
// Load from file
|
|
ChildMap = new CDataMember();
|
|
JSONparse->SetBase( ChildMap );
|
|
if (!JSONparse->ReadFromFile( NULL, Children->GetStr() )) {
|
|
if (Log) Log->Message( LogLevel, dlLow, "%s/%s: Fail to load parameters from file: '%s'",
|
|
ProcessName, Name, Children->GetStr() );
|
|
}
|
|
else {
|
|
ChildConfig = ChildMap->GetFirstChild();
|
|
while (ChildConfig) {
|
|
InitDeviceParam( Device, ChildConfig, FullName, DataPath, DataNode, ParamGroup );
|
|
ChildConfig = ChildConfig->GetNextPeer();
|
|
}
|
|
}
|
|
delete ChildMap;
|
|
}
|
|
else {
|
|
// Load children
|
|
ChildConfig = Children->GetFirstChild();
|
|
while (ChildConfig) {
|
|
InitDeviceParam( Device, ChildConfig, FullName, DataPath, DataNode, ParamGroup );
|
|
ChildConfig = ChildConfig->GetNextPeer();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Data Leaf
|
|
DataType = GetDataType((char*)ParamConfig->GetChStr( "Type", "Unsigned16", true ));
|
|
if ((Param = AddDeviceParam( Device, FullName, DataType, 1 ))) {
|
|
// Leaf
|
|
Read = ParamConfig->GetChBool( "Read", false, true );
|
|
Write = ParamConfig->GetChBool( "Write", false, true );
|
|
SetParamAccess( Param, Read, Write );
|
|
|
|
SetDataPath( Param, DataPath, DataNode );
|
|
|
|
if ((InitVal = ParamConfig->GetChild( "InitValue", false ))) {
|
|
if ((DataType == dtUnsigned16) || (DataType == dtUnsigned32_HL) || (DataType == dtUnsigned32_LH)) {
|
|
UpdateUnsignedValue( Param, InitVal->GetInt(0), Read );
|
|
if (Write)
|
|
SetUnsignedValue( Param, InitVal->GetInt(0), true );
|
|
}
|
|
else if ((DataType == dtSigned16) || (DataType == dtSigned32_HL) || (DataType == dtSigned32_LH)) {
|
|
UpdateSignedValue( Param, InitVal->GetInt(0), Read );
|
|
if (Write)
|
|
SetSignedValue( Param, InitVal->GetInt(0), true );
|
|
}
|
|
else if ((DataType == dtFloat32_L) || (DataType == dtFloat32_B)) {
|
|
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 );
|
|
}
|
|
}
|
|
|
|
if ((ParamGroupName = (char*)ParamConfig->GetChStr( "ParamGroup", NULL, false ))) {
|
|
ParamGroup = AddParamGroup( Device, ParamGroupName );
|
|
} else {
|
|
ParamGroup = ParentParamGroup;
|
|
}
|
|
if (ParamGroup)
|
|
AddParamItem( ParamGroup, Param );
|
|
|
|
EventOut = (char*)ParamConfig->GetChStr( "EventChannel", NULL, false );
|
|
EventInt = ParamConfig->GetChInt( "EventInterval", 0, false );
|
|
SetParamEvent( Param, EventOut, EventInt );
|
|
}
|
|
}
|
|
|
|
if (FullName) free( FullName );
|
|
if (DataPath) free( DataPath );
|
|
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;
|
|
InvalidReply = false;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void CDeviceCore::ValidReplyReceived()
|
|
{
|
|
// Reset parameters for next command
|
|
WaitingForReply = false;
|
|
InvalidReply = false;
|
|
PollRetry = 0;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CDeviceCore::CheckReplyTimeout()
|
|
{
|
|
// Check for Reply timeout
|
|
if (!WaitingForReply || !Timeout( PollWait, ReplyTimeout )) {
|
|
// No timeout
|
|
return false;
|
|
}
|
|
|
|
// Update flags
|
|
WaitingForReply = false;
|
|
InvalidReply = false;
|
|
PollRetry++;
|
|
|
|
// Handle No Reply / Retry
|
|
if (PollRetry > MaxRetries)
|
|
{
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Device '%s' %s - Max retries",
|
|
ProcessName, Name, DeviceChannel->Name, ActiveDevice->Name,
|
|
((InvalidReply)? "invalid reply" : "timeout"), PollRetry );
|
|
|
|
// Set Device Offline
|
|
DeviceOnline( ActiveDevice, false );
|
|
PollRetry = 0;
|
|
return true;
|
|
}
|
|
|
|
// Try again
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Device '%s' %s, Retry %d/%d",
|
|
ProcessName, Name, DeviceChannel->Name, ActiveDevice->Name,
|
|
((InvalidReply)? "invalid reply" : "timeout"), PollRetry, MaxRetries );
|
|
return false;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
TDevice * CDeviceCore::AddDeviceType( const char * DeviceTypeName, const char * DataPath )
|
|
{
|
|
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 = strdup( DeviceTypeName );
|
|
(*DeviceType)->DataPath = strdup( DataPath );
|
|
|
|
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 * DeviceType,
|
|
const int DeviceID, const char * DeviceAddress, const char * DataPath )
|
|
{
|
|
TDevice ** Device = &FirstDevice;
|
|
|
|
if (!DeviceName || !*DeviceName || (DeviceAddress && !*DeviceAddress)) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Could not add device '%s' - Invalid parameters, Tp:'%s' ID:%d, Addr:'%s'",
|
|
ProcessName, Name, ((DeviceName)? DeviceName : "[no name]"), ((DeviceType)? DeviceType : "[none]"),
|
|
DeviceID, ((DeviceAddress)? DeviceAddress : "[none]") );
|
|
return NULL;
|
|
}
|
|
|
|
// Find device matching by any parameter
|
|
while (*Device && strcasecmp((*Device)->Name, DeviceName) && (!DeviceID ||
|
|
(*Device)->ID != DeviceID) && (!DeviceAddress || strcasecmp((*Device)->Address, DeviceAddress)))
|
|
Device = &((*Device)->Next);
|
|
|
|
if (*Device) {
|
|
if (!strcasecmp((*Device)->Name, DeviceName)) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Could not add device '%s' - Name already exists",
|
|
ProcessName, Name, DeviceName );
|
|
}
|
|
else if (DeviceID && ((*Device)->ID == DeviceID)) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Could not add device '%s' - ID '%d' already exists",
|
|
ProcessName, Name, DeviceName, DeviceID );
|
|
}
|
|
else if (DeviceAddress && !strcasecmp((*Device)->Address, DeviceAddress)) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Could not add device '%s' - Address '%s' already exists",
|
|
ProcessName, Name, DeviceName, DeviceAddress );
|
|
}
|
|
return NULL;
|
|
}
|
|
else {
|
|
*Device = new TDevice;
|
|
|
|
(*Device)->Name = strdup( DeviceName );
|
|
(*Device)->ID = DeviceID;
|
|
|
|
if (DeviceAddress) {
|
|
(*Device)->Address = strdup( DeviceAddress );
|
|
}
|
|
|
|
if (DeviceType && *DeviceType) {
|
|
(*Device)->Type = strdup( DeviceType );
|
|
(*Device)->Template = GetDeviceType( DeviceType );
|
|
|
|
if (DataPath && *DataPath) {
|
|
(*Device)->DataPath = strdup( DataPath );
|
|
(*Device)->DataNode = ValueTree->GetChild( (*Device)->DataPath, true );
|
|
}
|
|
else if ((*Device)->Template && (*Device)->Template->DataPath) {
|
|
// Use Template path as prefix, then add device ID or address
|
|
int PathLen = strlen((*Device)->Template->DataPath) + strlen(DeviceName) + 1;
|
|
(*Device)->DataPath = (char*)malloc(PathLen +1);
|
|
sprintf( (*Device)->DataPath, "%s/%s", (*Device)->Template->DataPath, DeviceName );
|
|
(*Device)->DataNode = ValueTree->GetChild( (*Device)->DataPath, true );
|
|
}
|
|
}
|
|
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Added device '%s' - Tp:'%s'%s, ID:%d, Addr:'%s'",
|
|
ProcessName, Name, DeviceName, ((DeviceType)? DeviceType : "[none]"),
|
|
(((*Device)->Template)? "*" : ""), DeviceID, ((DeviceAddress)? DeviceAddress : "[none]") );
|
|
}
|
|
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)->Address)
|
|
free( (*Device)->Address );
|
|
if ((*Device)->Type)
|
|
free( (*Device)->Type );
|
|
if ((*Device)->DataPath)
|
|
free( (*Device)->DataPath );
|
|
|
|
// 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: Could not add Param '%s/%s' - Invalid parameters, Tp:'%s'",
|
|
ProcessName, Name, ((!Device)? "[no name]" : (Device->Name)? Device->Name : Device->Type),
|
|
((!ParamName)? "[no name]" : ParamName), DataTypeName[DataType]);
|
|
return NULL;
|
|
}
|
|
else if (*Param) {
|
|
// Parameter already exists
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Could not add Param '%s/%s' - Already exists",
|
|
ProcessName, Name, ((Device->Name)? Device->Name : Device->Type),
|
|
ParamName, DataTypeName[DataType]);
|
|
return NULL;
|
|
}
|
|
else {
|
|
// Create register
|
|
*Param = new TDeviceParam;
|
|
|
|
// Set Name
|
|
(*Param)->Name = strdup( 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_HL :
|
|
case dtUnsigned32_LH :
|
|
// 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_HL :
|
|
case dtSigned32_LH :
|
|
// 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_L :
|
|
case dtFloat32_B :
|
|
// 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', Tp:'%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 );
|
|
if ((*Param)->DataPath)
|
|
free( (*Param)->DataPath );
|
|
|
|
// 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 = strdup( 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, TDeviceParam * Param )
|
|
{
|
|
TDeviceParamItem ** Item;
|
|
|
|
if (!Param || !(Item = GetParamItemPtr( Group, Param->Name ))) {
|
|
// 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), ((!Param->Name)? "[param]" : Param->Name));
|
|
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, Param->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, Param->Name );
|
|
}
|
|
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::SetDataPath( TDeviceParam * Param, const char * DataPath, CDataMember * DataNode )
|
|
{
|
|
if (!Param)
|
|
return false;
|
|
|
|
Param->DataPath = strdup( DataPath );
|
|
Param->DataNode = DataNode;
|
|
|
|
if (Log) {
|
|
char * DeviceName = (Param->Device->Name)? Param->Device->Name : Param->Device->Type;
|
|
Log->Message( LogLevel, dlMedium, "%s/%s: Set param '%s/%s' data ref - P:%s, N:%s",
|
|
ProcessName, Name, DeviceName, Param->Name, DataPath, ((DataNode)? DataNode->GetName() : "(none)") );
|
|
}
|
|
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;
|
|
|
|
if (Param->DataNode)
|
|
Param->DataNode->SetInt( *((u_int16_t*)Param->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_HL :
|
|
case dtUnsigned32_LH :
|
|
if (Init || (*((u_int32_t*)Param->Value) != Value))
|
|
{
|
|
// Set new value & mark change
|
|
*((u_int32_t*)Param->Value) = Value;
|
|
|
|
if (Param->DataNode)
|
|
Param->DataNode->SetInt( *((u_int32_t*)Param->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_L :
|
|
case dtFloat32_B :
|
|
// 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;
|
|
|
|
if (Param->DataNode)
|
|
Param->DataNode->SetInt( *((u_int32_t*)Param->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;
|
|
|
|
if (Param->DataNode)
|
|
Param->DataNode->SetInt( *((int16_t*)Param->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_HL :
|
|
case dtSigned32_LH :
|
|
if (Init || (*((int32_t*)Param->Value) != Value))
|
|
{
|
|
// Set new value & mark change
|
|
*((int32_t*)Param->Value) = Value;
|
|
|
|
if (Param->DataNode)
|
|
Param->DataNode->SetInt( *((int32_t*)Param->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_L :
|
|
case dtFloat32_B :
|
|
if (Init || (*((float*)Param->Value) != Value))
|
|
{
|
|
// Set new value & mark change
|
|
*((float*)Param->Value) = Value;
|
|
|
|
if (Param->DataNode)
|
|
Param->DataNode->SetFloat( *((float*)Param->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
|
|
}
|
|
|
|
if (Param->DataNode)
|
|
Param->DataNode->SetStr( (char*)Param->Value );
|
|
|
|
// 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_HL :
|
|
case dtUnsigned32_LH :
|
|
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_HL :
|
|
case dtSigned32_LH :
|
|
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_L :
|
|
case dtFloat32_B :
|
|
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 = strndup( Value, Len );
|
|
TempStr[Len] = 0;
|
|
UseTempStr = true;
|
|
}
|
|
|
|
// Convert to correct type
|
|
switch (Param->DataType)
|
|
{
|
|
case dtUnsigned16 :
|
|
case dtUnsigned32_HL :
|
|
case dtUnsigned32_LH :
|
|
UnsignedVal = (u_int32_t)strtoul( ((UseTempStr)? TempStr : Value), NULL, 10 );
|
|
SetUnsignedValue( Param, UnsignedVal, Force );
|
|
break;
|
|
|
|
case dtSigned16 :
|
|
case dtSigned32_HL :
|
|
case dtSigned32_LH :
|
|
SignedVal = (u_int32_t)strtol( ((UseTempStr)? TempStr : Value), NULL, 10 );
|
|
SetUnsignedValue( Param, SignedVal, Force );
|
|
break;
|
|
|
|
case dtFloat32_L :
|
|
case dtFloat32_B :
|
|
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) {
|
|
Value[0] = 0;
|
|
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_HL :
|
|
case dtUnsigned32_LH :
|
|
sprintf( Value, "%u", (*((u_int32_t*)Param->Value)) );
|
|
break;
|
|
|
|
case dtSigned16 :
|
|
sprintf( Value, "%d", (*((int16_t*)Param->Value)) );
|
|
break;
|
|
|
|
case dtSigned32_HL :
|
|
case dtSigned32_LH :
|
|
sprintf( Value, "%d", (*((int32_t*)Param->Value)) );
|
|
break;
|
|
|
|
case dtFloat32_L :
|
|
case dtFloat32_B :
|
|
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 :
|
|
Value[0] = 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::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 (!Start || !*Start) {
|
|
Param[0] = 0;
|
|
NextParam = NULL;
|
|
return false;
|
|
}
|
|
else 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 Value[50];
|
|
char * NextParam = NULL;
|
|
TDevice * Device = NULL;
|
|
TDeviceParam * Param = NULL;
|
|
char OutputStr[250];
|
|
bool Error = false;
|
|
|
|
// Show accepted input
|
|
Log->Output( LogLevel, dlHigh, loNormal, Data, MaxLen, "%s/%s: Channel '%s' - IN:",
|
|
ProcessName, Name, ChannelName );
|
|
|
|
// Get command command
|
|
GetCmdParam( Data, Value, &NextParam );
|
|
if (!strcasecmp( "get", Value ))
|
|
{
|
|
// Validate parameters
|
|
if (!GetCmdParam( NextParam, Value, &NextParam)) {
|
|
strcpy( OutputStr, "- Missing device name" );
|
|
Error = true;
|
|
}
|
|
else if (!(Device = GetDeviceByName( Value ))) {
|
|
strcpy( OutputStr, "- Device not found");
|
|
Error = true;
|
|
}
|
|
else if (!GetCmdParam( NextParam, Value, &NextParam )) {
|
|
strcpy( OutputStr, "- Missing parameter name" );
|
|
Error = true;
|
|
}
|
|
else if (!(Param = GetDeviceParam( Device, Value ))) {
|
|
strcpy( OutputStr, "- Device parameter not found");
|
|
Error = true;
|
|
}
|
|
else if (NextParam) {
|
|
strcpy( OutputStr, "- Too many parameters");
|
|
Error = true;
|
|
}
|
|
else {
|
|
sprintf( OutputStr, "> %s,%s = ", Device->Name, Param->Name );
|
|
Len = MaxLen - strlen(OutputStr) - 1;
|
|
|
|
if (!GetValue( (TDeviceParam*)Param, &OutputStr[ strlen(OutputStr) ], Len )) {
|
|
strcpy( OutputStr, "- Error getting parameter");
|
|
Error = true;
|
|
}
|
|
else {
|
|
strcat( OutputStr, "\n" );
|
|
Output( ChannelName, OutputStr, strlen(OutputStr) );
|
|
}
|
|
}
|
|
|
|
// Report error
|
|
if (Error) {
|
|
Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' Get",
|
|
ProcessName, Name, ChannelName, OutputStr );
|
|
strcat( OutputStr, "\n" );
|
|
Output( ChannelName, OutputStr, strlen(OutputStr) );
|
|
}
|
|
}
|
|
else if (!strcasecmp( "set", Value ))
|
|
{
|
|
// Validate parameters
|
|
if (!GetCmdParam( NextParam, Value, &NextParam)) {
|
|
strcpy( OutputStr, "- Missing device name" );
|
|
Error = true;
|
|
}
|
|
else if (!(Device = GetDeviceByName( Value ))) {
|
|
strcpy( OutputStr, "- Device not found");
|
|
Error = true;
|
|
}
|
|
else if (!GetCmdParam( NextParam, Value, &NextParam )) {
|
|
strcpy( OutputStr, "- Missing parameter name" );
|
|
Error = true;
|
|
}
|
|
else if (!(Param = GetDeviceParam( Device, Value ))) {
|
|
strcpy( OutputStr, "- Device parameter not found");
|
|
Error = true;
|
|
}
|
|
else if (!GetCmdParam( NextParam, Value, &NextParam )) {
|
|
strcpy( OutputStr, "- Missing set value");
|
|
Error = true;
|
|
}
|
|
else if (NextParam) {
|
|
strcpy( OutputStr, "- Too many parameters");
|
|
Error = true;
|
|
}
|
|
else {
|
|
if (!SetValue( Param, Value, strlen(Value), true )) {
|
|
strcpy( OutputStr, "- Error seting parameter");
|
|
Error = true;
|
|
}
|
|
else {
|
|
sprintf( OutputStr, "> set %s,%s = %s\n", Device->Name, Param->Name, Value );
|
|
Output( ChannelName, OutputStr );
|
|
}
|
|
}
|
|
|
|
// Report error
|
|
if (Error) {
|
|
Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' Set",
|
|
ProcessName, Name, ChannelName, OutputStr );
|
|
strcat( OutputStr, "\n" );
|
|
Output( ChannelName, OutputStr, strlen(OutputStr) );
|
|
}
|
|
}
|
|
else if (!strcasecmp( "status", Value ))
|
|
{
|
|
// Validate parameters
|
|
if (!GetCmdParam( NextParam, Value, &NextParam)) {
|
|
strcpy( OutputStr, "- Missing device name" );
|
|
Error = true;
|
|
}
|
|
else if (!(Device = GetDeviceByName( Value ))) {
|
|
strcpy( OutputStr, "- Device not found");
|
|
Error = true;
|
|
}
|
|
else if (NextParam) {
|
|
strcpy( OutputStr, "- Too many parameters");
|
|
Error = true;
|
|
}
|
|
else {
|
|
sprintf( OutputStr, "> %s - %s\n", Device->Name, ((Device->Online)? "online" : "offline") );
|
|
Output( ChannelName, OutputStr );
|
|
}
|
|
|
|
// Report error
|
|
if (Error) {
|
|
Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s' Status",
|
|
ProcessName, Name, ChannelName, OutputStr );
|
|
strcat( OutputStr, "\n" );
|
|
Output( ChannelName, OutputStr, strlen(OutputStr) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Unrecognised command
|
|
strcpy( OutputStr, "- Unknown command");
|
|
Log->Message( LogLevel, dlMedium, "%s/%s: Channel '%s', %s%s",
|
|
ProcessName, Name, ChannelName, Value, OutputStr );
|
|
strcat( OutputStr, " (comma separated values)\n" );
|
|
Output( ChannelName, OutputStr, strlen(OutputStr) );
|
|
}
|
|
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_HL :
|
|
case dtUnsigned32_LH :
|
|
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_HL :
|
|
case dtSigned32_LH :
|
|
sprintf( Message, "%s: %d\n", Param->Name, *((int32_t*)Param->Value) );
|
|
break;
|
|
|
|
case dtFloat32_L :
|
|
case dtFloat32_B :
|
|
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;
|
|
}
|
|
//---------------------------------------------------------------------------
|