#include <cstring>
#include "Group.h"
#include "Manager.h"
#include "Driver.h"
#include "Node.h"
#include "Notification.h"
#include "Options.h"
#include "command_classes/Association.h"
#include "command_classes/AssociationCommandConfiguration.h"
#include "command_classes/MultiInstanceAssociation.h"
#include "platform/Log.h"
#include "tinyxml.h"
using namespace OpenZWave;
Group::Group
(
uint32 const _homeId,
uint8 const _nodeId,
uint8 const _groupIdx,
uint8 const _maxAssociations
):
m_homeId( _homeId ),
m_nodeId( _nodeId ),
m_groupIdx( _groupIdx ),
m_maxAssociations( _maxAssociations ),
m_auto( false ),
m_multiInstance( false )
{
char str[16];
snprintf( str, sizeof(str), "Group %d", m_groupIdx );
m_label = str;
CheckAuto();
}
Group::Group
(
uint32 const _homeId,
uint8 const _nodeId,
TiXmlElement const* _groupElement
):
m_homeId( _homeId ),
m_nodeId( _nodeId ),
m_groupIdx( 0 ),
m_maxAssociations( 0 ),
m_auto( false ),
m_multiInstance( false )
{
int intVal;
char const* str;
vector<InstanceAssociation> pending;
if( TIXML_SUCCESS == _groupElement->QueryIntAttribute( "index", &intVal ) )
{
m_groupIdx = (uint8)intVal;
}
CheckAuto();
if( TIXML_SUCCESS == _groupElement->QueryIntAttribute( "max_associations", &intVal ) )
{
m_maxAssociations = (uint8)intVal;
}
str = _groupElement->Attribute( "auto" );
if( str )
{
m_auto = !strcmp( str, "true" );
}
str = _groupElement->Attribute( "label" );
if( str )
{
m_label = str;
}
str = _groupElement->Attribute( "multiInstance" );
if( str )
{
m_multiInstance = !strcmp( str, "true" );
}
TiXmlElement const* associationElement = _groupElement->FirstChildElement();
while( associationElement )
{
char const* elementName = associationElement->Value();
if( elementName && !strcmp( elementName, "Node" ) )
{
if( associationElement->QueryIntAttribute( "id", &intVal ) == TIXML_SUCCESS )
{
InstanceAssociation association;
association.m_nodeId = (uint8)intVal;
if( associationElement->QueryIntAttribute( "instance", &intVal ) == TIXML_SUCCESS )
association.m_instance = (uint8)intVal;
else
association.m_instance = 0x00;
pending.push_back( association );
}
}
associationElement = associationElement->NextSiblingElement();
}
OnGroupChanged( pending );
}
void Group::CheckAuto
(
)
{
if( m_groupIdx == 255 )
{
m_auto = true;
}
else if( m_groupIdx == 1 )
{
m_auto = true;
if( Driver* driver = Manager::Get()->GetDriver( m_homeId ) )
{
if( Node* node = driver->GetNodeUnsafe( m_nodeId ) )
{
if( Group* group = node->GetGroup( 255 ) )
{
group->SetAuto( false );
}
}
}
}
}
void Group::WriteXML
(
TiXmlElement* _groupElement
)
{
char str[16];
snprintf( str, 16, "%d", m_groupIdx );
_groupElement->SetAttribute( "index", str );
snprintf( str, 16, "%d", m_maxAssociations );
_groupElement->SetAttribute( "max_associations", str );
_groupElement->SetAttribute( "label", m_label.c_str() );
_groupElement->SetAttribute( "auto", m_auto ? "true" : "false" );
if( m_multiInstance )
{
_groupElement->SetAttribute( "multiInstance", m_multiInstance ? "true" : "false" );
}
for( map<InstanceAssociation,AssociationCommandVec,classcomp>::iterator it = m_associations.begin(); it != m_associations.end(); ++it )
{
TiXmlElement* associationElement = new TiXmlElement( "Node" );
snprintf( str, 16, "%d", it->first.m_nodeId );
associationElement->SetAttribute( "id", str );
if (it->first.m_instance != 0)
{
snprintf( str, 16, "%d", it->first.m_instance );
associationElement->SetAttribute( "instance", str );
}
_groupElement->LinkEndChild( associationElement );
}
}
bool Group::Contains
(
uint8 const _nodeId,
uint8 const _instance
)
{
for( map<InstanceAssociation,AssociationCommandVec,classcomp>::iterator it = m_associations.begin(); it != m_associations.end(); ++it )
{
if ((it->first.m_nodeId == _nodeId) && (it->first.m_instance == _instance))
{
return true;
}
}
return false;
}
void Group::AddAssociation
(
uint8 const _nodeId,
uint8 const _instance
)
{
if( Driver* driver = Manager::Get()->GetDriver( m_homeId ) )
{
if( Node* node = driver->GetNodeUnsafe( m_nodeId ) )
{
MultiInstanceAssociation* cc = static_cast<MultiInstanceAssociation*>( node->GetCommandClass( MultiInstanceAssociation::StaticGetCommandClassId() ));
if( cc && IsMultiInstance() )
{
cc->Set( m_groupIdx, _nodeId, _instance );
cc->QueryGroup( m_groupIdx, 0 );
}
else if( Association* cc = static_cast<Association*>( node->GetCommandClass( Association::StaticGetCommandClassId() ) ) )
{
cc->Set( m_groupIdx, _nodeId );
cc->QueryGroup( m_groupIdx, 0 );
}
else
{
Log::Write( LogLevel_Info, m_nodeId, "No supported Association CC found" );
}
}
}
}
void Group::RemoveAssociation
(
uint8 const _nodeId,
uint8 const _instance
)
{
if( Driver* driver = Manager::Get()->GetDriver( m_homeId ) )
{
if( Node* node = driver->GetNodeUnsafe( m_nodeId ) )
{
MultiInstanceAssociation* cc = static_cast<MultiInstanceAssociation*>( node->GetCommandClass( MultiInstanceAssociation::StaticGetCommandClassId() ));
if( cc && IsMultiInstance() )
{
cc->Remove( m_groupIdx, _nodeId, _instance );
cc->QueryGroup( m_groupIdx, 0 );
}
else if( Association* cc = static_cast<Association*>( node->GetCommandClass( Association::StaticGetCommandClassId() ) ) )
{
cc->Remove( m_groupIdx, _nodeId );
cc->QueryGroup( m_groupIdx, 0 );
}
else
{
Log::Write( LogLevel_Info, m_nodeId, "No supported Association CC found" );
}
}
}
}
void Group::OnGroupChanged
(
vector<uint8> const& _associations
)
{
vector<InstanceAssociation> instanceAssociations;
uint8 i;
for( i=0; i<_associations.size(); ++i )
{
InstanceAssociation association;
association.m_nodeId = _associations[i];
association.m_instance = 0x00;
instanceAssociations.push_back( association );
}
OnGroupChanged(instanceAssociations);
instanceAssociations.clear();
}
void Group::OnGroupChanged
(
vector<InstanceAssociation> const& _associations
)
{
bool notify = false;
if( _associations.size() != m_associations.size() )
{
m_associations.clear();
notify = true;
}
else
{
if ( _associations.size() == 0 && m_associations.size() == 0 )
{
notify = true;
}
}
uint8 oldSize = (uint8)m_associations.size();
uint8 i;
for( i=0; i<_associations.size(); ++i )
{
m_associations[_associations[i]] = AssociationCommandVec();
}
if( (!notify) && ( oldSize != m_associations.size() ) )
{
m_associations.clear();
for( i=0; i<_associations.size(); ++i )
{
m_associations[_associations[i]] = AssociationCommandVec();
}
notify = true;
}
if( notify )
{
if( Driver* driver = Manager::Get()->GetDriver( m_homeId ) )
{
if( Node* node = driver->GetNodeUnsafe( m_nodeId ) )
{
if( AssociationCommandConfiguration* cc = static_cast<AssociationCommandConfiguration*>( node->GetCommandClass( AssociationCommandConfiguration::StaticGetCommandClassId() ) ) )
{
for( map<InstanceAssociation,AssociationCommandVec,classcomp>::iterator it = m_associations.begin(); it != m_associations.end(); ++it )
{
cc->RequestCommands( m_groupIdx, it->first.m_nodeId );
}
}
}
}
Notification* notification = new Notification( Notification::Type_Group );
notification->SetHomeAndNodeIds( m_homeId, m_nodeId );
notification->SetGroupIdx( m_groupIdx );
Manager::Get()->GetDriver( m_homeId )->QueueNotification( notification );
bool update = false;
Options::Get()->GetOptionAsBool( "PerformReturnRoutes", &update );
if( update )
{
Driver *drv = Manager::Get()->GetDriver( m_homeId );
if (drv)
drv->UpdateNodeRoutes( m_nodeId );
}
}
}
uint32 Group::GetAssociations
(
uint8** o_associations
)
{
size_t numNodes = m_associations.size();
if( !numNodes )
{
*o_associations = NULL;
return 0;
}
uint8* associations = new uint8[numNodes]; uint32 i = 0;
for( map<InstanceAssociation,AssociationCommandVec,classcomp>::iterator it = m_associations.begin(); it != m_associations.end(); ++it )
{
if ( it->first.m_instance == 0x00)
{
associations[i++] = it->first.m_nodeId;
}
}
*o_associations = associations;
return (uint32) i;
}
uint32 Group::GetAssociations
(
InstanceAssociation** o_associations
)
{
size_t numNodes = m_associations.size();
if( !numNodes )
{
*o_associations = NULL;
return 0;
}
InstanceAssociation* associations = new InstanceAssociation[numNodes];
uint32 i = 0;
for( map<InstanceAssociation,AssociationCommandVec,classcomp>::iterator it = m_associations.begin(); it != m_associations.end(); ++it )
{
associations[i++] = it->first;
}
*o_associations = associations;
return (uint32) numNodes;
}
bool Group::ClearCommands
(
uint8 const _nodeId,
uint8 const _instance
)
{
for( map<InstanceAssociation,AssociationCommandVec,classcomp>::iterator it = m_associations.begin(); it != m_associations.end(); ++it )
{
if( (it->first.m_nodeId == _nodeId) && (it->first.m_instance == _instance) )
{
it->second.clear();
return true;
}
}
return false;
}
bool Group::AddCommand
(
uint8 const _nodeId,
uint8 const _length,
uint8 const* _data,
uint8 const _instance
)
{
for( map<InstanceAssociation,AssociationCommandVec,classcomp>::iterator it = m_associations.begin(); it != m_associations.end(); ++it )
{
if( (it->first.m_nodeId == _nodeId) && (it->first.m_instance == _instance) )
{
it->second.push_back( AssociationCommand( _length, _data ) );
return true;
}
}
return false;
}
Group::AssociationCommand::AssociationCommand
(
uint8 const _length,
uint8 const* _data
):
m_length( _length )
{
m_data = new uint8[_length];
memcpy( m_data, _data, _length );
}
Group::AssociationCommand::~AssociationCommand
(
)
{
delete [] m_data;
}