text: //-----------------------------------------------------------------------------
  //
  //	MultiInstance.cpp
  //
  //	Implementation of the Z-Wave COMMAND_CLASS_MULTI_INSTANCE
  //
  //	Copyright (c) 2010 Mal Lansell <openzwave@lansell.org>
  //
  //	SOFTWARE NOTICE AND LICENSE
  //
  //	This file is part of OpenZWave.
  //
  //	OpenZWave is free software: you can redistribute it and/or modify
  //	it under the terms of the GNU Lesser General Public License as published
  //	by the Free Software Foundation, either version 3 of the License,
  //	or (at your option) any later version.
  //
  //	OpenZWave is distributed in the hope that it will be useful,
  //	but WITHOUT ANY WARRANTY; without even the implied warranty of
  //	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  //	GNU Lesser General Public License for more details.
  //
  //	You should have received a copy of the GNU Lesser General Public License
  //	along with OpenZWave.  If not, see <http://www.gnu.org/licenses/>.
  //
  //-----------------------------------------------------------------------------
  
  #include "tinyxml.h"
  #include "command_classes/CommandClasses.h"
  #include "command_classes/Basic.h"
  #include "command_classes/MultiInstance.h"
  #include "command_classes/NoOperation.h"
  #include "Defs.h"
  #include "Msg.h"
  #include "Driver.h"
  #include "Node.h"
  #include "platform/Log.h"
  
  using namespace OpenZWave;
  
  // Reduced set of Generic Device classes sorted to reduce
  // the likely number of calls to MultiChannelCmd_EndPointFind.
  uint8 const c_genericClass[] =
  {
  		0x21,		// Multilevel Sensor
  		0x20,		// Binary Sensor
  		0x31,		// Meter
  		0x08,		// Thermostat
  		0x11,		// Multilevel Switch
  		0x10,		// Binary Switch
  		0x12,		// Remote Switch
  		0xa1,		// Alarm Sensor
  		0x16,		// Ventilation
  		0x30,		// Pulse Meter
  		0x40,		// Entry Control
  		0x13,		// Toggle Switch
  		0x03,		// AV Control Point
  		0x04,		// Display
  		0x00		// End of list
  };
  
  char const* c_genericClassName[] =
  {
  		"Multilevel Sensor",
  		"Binary Sensor",
  		"Meter",
  		"Thermostat",
  		"Multilevel Switch",
  		"Binary Switch",
  		"Remote Switch",
  		"Alarm Sensor",
  		"Ventilation",
  		"Pulse Meter",
  		"Entry Control",
  		"Toggle Switch",
  		"AV Control Point",
  		"Display",
  		"Unknown"
  };
  
  //-----------------------------------------------------------------------------
  // <MultiInstance::MultiInstance>
  // Constructor
  //-----------------------------------------------------------------------------
  MultiInstance::MultiInstance
  (
  		uint32 const _homeId,
  		uint8 const _nodeId
  ):
  CommandClass( _homeId, _nodeId ),
  m_numEndPoints( 0 ),
  m_numEndPointsHint( 0 ),
  m_endPointMap( MultiInstanceMapAll ),
  m_endPointFindSupported( false ),
  m_uniqueendpoints( false )
  {
  }
  
  //-----------------------------------------------------------------------------
  // <MultiInstance::ReadXML>
  // Class specific configuration
  //-----------------------------------------------------------------------------
  void MultiInstance::ReadXML
  (
  		TiXmlElement const* _ccElement
  )
  {
  	int32 intVal;
  	char const* str;
  
  	CommandClass::ReadXML( _ccElement );
  
  	if( TIXML_SUCCESS == _ccElement->QueryIntAttribute( "endpoints", &intVal ) )
  	{
  		m_numEndPointsHint = (uint8)intVal;
  	}
  
  	str = _ccElement->Attribute("mapping");
  	if( str )
  	{
  		if( strcmp( str, "all") == 0 )
  		{
  			m_endPointMap = MultiInstanceMapAll;
  		}
  		else if( strcmp( str, "endpoints") == 0 )
  		{
  			m_endPointMap = MultiInstanceMapEndPoints;
  		}
  		else
  		{
  			Log::Write( LogLevel_Info, GetNodeId(), "Bad value for mapping: %s", str);
  		}
  	}
  
  	str = _ccElement->Attribute("findsupport");
  	if( str )
  	{
  		m_endPointFindSupported = !strcmp( str, "true");
  	}
  	str = _ccElement->Attribute("ignoreUnsolicitedMultiChnCapReport");
  	if( str )
  	{
  		m_ignoreUnsolicitedMultiChannelCapabilityReport = !strcmp( str, "true");
  	}
  	str = _ccElement->Attribute("forceUniqueEndpoints");
  	if( str )
  	{
  		m_uniqueendpoints = !strcmp( str, "true");
  	}
  }
  
  //-----------------------------------------------------------------------------
  // <MultiInstance::WriteXML>
  // Class specific configuration
  //-----------------------------------------------------------------------------
  void MultiInstance::WriteXML
  (
  		TiXmlElement* _ccElement
  )
  {
  	char str[32];
  
  	CommandClass::WriteXML( _ccElement );
  	if( m_numEndPointsHint != 0 )
  	{
  		snprintf( str, sizeof(str), "%d", m_numEndPointsHint );
  		_ccElement->SetAttribute( "endpoints", str);
  	}
  
  	if( m_endPointMap == MultiInstanceMapEndPoints )
  	{
  		_ccElement->SetAttribute( "mapping", "endpoints" );
  	}
  
  	if( m_endPointFindSupported )
  	{
  		_ccElement->SetAttribute( "findsupport", "true" );
  	}
  
  	if( m_uniqueendpoints )
  	{
  		_ccElement->SetAttribute( "forceUniqueEndpoints", "true" );
  	}
  
  }
  
  //-----------------------------------------------------------------------------
  // <MultiInstance::RequestInstances>
  // Request number of instances of the specified command class from the device
  //-----------------------------------------------------------------------------
  bool MultiInstance::RequestInstances
  (
  )
  {
  	bool res = false;
  
  	if( GetVersion() == 1 )
  	{
  		if( Node* node = GetNodeUnsafe() )
  		{
  			// MULTI_INSTANCE
  			for( map<uint8,CommandClass*>::const_iterator it = node->m_commandClassMap.begin(); it != node->m_commandClassMap.end(); ++it )
  			{
  				CommandClass* cc = it->second;
  				if( cc->GetCommandClassId() == NoOperation::StaticGetCommandClassId() )
  				{
  					continue;
  				}
  				if( cc->HasStaticRequest( StaticRequest_Instances ) )
  				{
  					Log::Write( LogLevel_Info, GetNodeId(), "MultiInstanceCmd_Get for %s", cc->GetCommandClassName().c_str() );
  
  					Msg* msg = new Msg( "MultiInstanceCmd_Get", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
  					msg->Append( GetNodeId() );
  					msg->Append( 3 );
  					msg->Append( GetCommandClassId() );
  					msg->Append( MultiInstanceCmd_Get );
  					msg->Append( cc->GetCommandClassId() );
  					msg->Append( GetDriver()->GetTransmitOptions() );
  					GetDriver()->SendMsg( msg, Driver::MsgQueue_Query );
  					res = true;
  				}
  			}
  		}
  	}
  	else
  	{
  		// MULTI_CHANNEL
  
  		Log::Write( LogLevel_Info, GetNodeId(), "MultiChannelCmd_EndPointGet for node %d", GetNodeId() );
  
  		Msg* msg = new Msg( "MultiChannelCmd_EndPointGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
  		msg->Append( GetNodeId() );
  		msg->Append( 2 );
  		msg->Append( GetCommandClassId() );
  		msg->Append( MultiChannelCmd_EndPointGet );
  		msg->Append( GetDriver()->GetTransmitOptions() );
  		GetDriver()->SendMsg( msg, Driver::MsgQueue_Query );
  		res = true;
  	}
  
  	return res;
  }
  
  //-----------------------------------------------------------------------------
  // <MultiInstance::HandleMsg>
  // Handle a message from the Z-Wave network
  //-----------------------------------------------------------------------------
  bool MultiInstance::HandleMsg
  (
  		uint8 const* _data,
  		uint32 const _length,
  		uint32 const _instance	// = 1
  )
  {
  	bool handled = false;
  	Node* node = GetNodeUnsafe();
  	if( node != NULL )
  	{
  		handled = true;
  		switch( (MultiInstanceCmd)_data[0] )
  		{
  			case MultiInstanceCmd_Report:
  			{
  				HandleMultiInstanceReport( _data, _length );
  				break;
  			}
  			case MultiInstanceCmd_Encap:
  			{
  				HandleMultiInstanceEncap( _data, _length );
  				break;
  			}
  			case MultiChannelCmd_EndPointReport:
  			{
  				HandleMultiChannelEndPointReport( _data, _length );
  				break;
  			}
  			case MultiChannelCmd_CapabilityReport:
  			{
  				HandleMultiChannelCapabilityReport( _data, _length );
  				break;
  			}
  			case MultiChannelCmd_EndPointFindReport:
  			{
  				HandleMultiChannelEndPointFindReport( _data, _length );
  				break;
  			}
  			case MultiChannelCmd_Encap:
  			{
  				HandleMultiChannelEncap( _data, _length );
  				break;
  			}
  			default:
  			{
  				handled = false;
  				break;
  			}
  		}
  	}
  
  	return handled;
  }
  
  //-----------------------------------------------------------------------------
  // <MultiInstance::HandleMultiInstanceReport>
  // Handle a message from the Z-Wave network
  //-----------------------------------------------------------------------------
  void MultiInstance::HandleMultiInstanceReport
  (
  		uint8 const* _data,
  		uint32 const _length
  )
  {
  	if( Node* node = GetNodeUnsafe() )
  	{
  		uint8 commandClassId = _data[1];
  		uint8 instances = _data[2];
  
  		if( CommandClass* pCommandClass = node->GetCommandClass( commandClassId ) )
  		{
  			Log::Write( LogLevel_Info, GetNodeId(), "Received MultiInstanceReport from node %d for %s: Number of instances = %d", GetNodeId(), pCommandClass->GetCommandClassName().c_str(), instances );
  			pCommandClass->SetInstances( instances );
  			pCommandClass->ClearStaticRequest( StaticRequest_Instances );
  		}
  	}
  }
  
  //-----------------------------------------------------------------------------
  // <MultiInstance::HandleMultiInstanceEncap>
  // Handle a message from the Z-Wave network
  //-----------------------------------------------------------------------------
  void MultiInstance::HandleMultiInstanceEncap
  (
  		uint8 const* _data,
  		uint32 const _length
  )
  {
  	if( Node* node = GetNodeUnsafe() )
  	{
  		uint8 instance = _data[1];
  		if( GetVersion() > 1 )
  		{
  			instance &= 0x7f;
  		}
  		uint8 commandClassId = _data[2];
  
  		if( CommandClass* pCommandClass = node->GetCommandClass( commandClassId ) )
  		{
  			Log::Write( LogLevel_Info, GetNodeId(), "Received a MultiInstanceEncap from node %d, instance %d, for Command Class %s", GetNodeId(), instance, pCommandClass->GetCommandClassName().c_str() );
  			pCommandClass->ReceivedCntIncr();
  			pCommandClass->HandleMsg( &_data[3], _length-3, instance );
  		}
  	}
  }
  
  //-----------------------------------------------------------------------------
  // <MultiInstance::HandleMultiChannelEndPointReport>
  // Handle a message from the Z-Wave network
  //-----------------------------------------------------------------------------
  void MultiInstance::HandleMultiChannelEndPointReport
  (
  		uint8 const* _data,
  		uint32 const _length
  )
  {
  	int len;
  
  	if( m_numEndPoints != 0 )
  	{
  		return;
  	}
  
  	m_numEndPointsCanChange = (( _data[1] & 0x80 ) != 0 );	// Number of endpoints can change.
  	m_endPointsAreSameClass = (( _data[1] & 0x40 ) != 0 );	// All endpoints are the same command class.
  
  	/* some devices (eg, Aeotec Smart Dimmer 6 incorrectly report all endpoints are the same */
  	if( m_uniqueendpoints )
  		m_endPointsAreSameClass = false;
  
  
  	m_numEndPoints = _data[2] & 0x7f;
  	if( m_numEndPointsHint != 0 )
  	{
  		m_numEndPoints = m_numEndPointsHint;		// don't use device's number
  	}
  
  	len = m_numEndPoints;
  	if( m_endPointsAreSameClass ) // only need to check single end point
  	{
  		len = 1;
  		Log::Write( LogLevel_Info, GetNodeId(), "Received MultiChannelEndPointReport from node %d. All %d endpoints are the same.", GetNodeId(), m_numEndPoints );
  	}
  	else
  	{
  		Log::Write( LogLevel_Info, GetNodeId(), "Received MultiChannelEndPointReport from node %d. %d endpoints are not all the same.", GetNodeId(), m_numEndPoints );
  	}
  
  	// This code assumes the endpoints are all in numeric sequential order.
  	// Since the end point finds do not appear to work this is the best estimate.
  	for( uint8 i = 1; i <= len; i++ )
  	{
  
  		// Send a single capability request to each endpoint
  		Log::Write( LogLevel_Info, GetNodeId(), "MultiChannelCmd_CapabilityGet for endpoint %d", i );
  		Msg* msg = new Msg( "MultiChannelCmd_CapabilityGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
  		msg->Append( GetNodeId() );
  		msg->Append( 3 );
  		msg->Append( GetCommandClassId() );
  		msg->Append( MultiChannelCmd_CapabilityGet );
  		msg->Append( i );
  		msg->Append( GetDriver()->GetTransmitOptions() );
  		GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
  	}
  }
  
  //-----------------------------------------------------------------------------
  // <MultiInstance::HandleMultiChannelCapabilityReport>
  // Handle a message from the Z-Wave network
  //-----------------------------------------------------------------------------
  void MultiInstance::HandleMultiChannelCapabilityReport
  (
  		uint8 const* _data,
  		uint32 const _length
  )
  {
  
  	bool dynamic = ((_data[1] & 0x80)!=0);
  
  
  	if( Node* node = GetNodeUnsafe() )
  	{
  		/* if you having problems with Dynamic Devices not correctly
  		 * updating the commandclasses, see this email thread:
  		 * https://groups.google.com/d/topic/openzwave/IwepxScRAVo/discussion
  		 */
  		if ((m_ignoreUnsolicitedMultiChannelCapabilityReport && (node->GetCurrentQueryStage() != Node::QueryStage_Instances))
  				&& !dynamic && m_endPointCommandClasses.size() > 0) {
  			Log::Write(LogLevel_Error, GetNodeId(), "Received a Unsolicited MultiChannelEncap when we are not in QueryState_Instances");
  			return;
  		}
  
  		uint8 endPoint = _data[1] & 0x7f;
  
  		Log::Write( LogLevel_Info, GetNodeId(), "Received MultiChannelCapabilityReport from node %d for endpoint %d", GetNodeId(), endPoint );
  		Log::Write( LogLevel_Info, GetNodeId(), "    Endpoint is%sdynamic, and is a %s", dynamic ? " " : " not ", node->GetEndPointDeviceClassLabel( _data[2], _data[3] ).c_str() );
  		Log::Write( LogLevel_Info, GetNodeId(), "    Command classes supported by the endpoint are:" );
  
  		// Store the command classes for later use
  		bool afterMark = false;
  		m_endPointCommandClasses.clear();
  		uint8 numCommandClasses = _length - 5;
  		for( uint8 i = 0; i < numCommandClasses; ++i )
  		{
  			uint8 commandClassId = _data[i+4];
  			if( commandClassId == 0xef )
  			{
  				afterMark = true;
  				continue;
  			}
  
  			m_endPointCommandClasses.insert( commandClassId );
  
  			// Ensure the node supports this command class
  			CommandClass* cc = node->GetCommandClass( commandClassId );
  			if( !cc )
  			{
  				cc = node->AddCommandClass( commandClassId );
  				if( cc && afterMark )
  				{
  					cc->SetAfterMark();
  				}
  			}
  			if( cc )
  			{
  				Log::Write( LogLevel_Info, GetNodeId(), "        %s", cc->GetCommandClassName().c_str() );
  			}
  		}
  
  		// Create internal library instances for each command class in the list
  		// Also set up mapping from intances to endpoints for encapsulation
  		Basic* basic = static_cast<Basic*>( node->GetCommandClass( Basic::StaticGetCommandClassId() ) );
  		if( m_endPointsAreSameClass )				// Create all the same instances here
  		{
  			int len;
  
  			if( m_endPointMap == MultiInstanceMapAll )	// Include the non-endpoint instance
  			{
  				endPoint = 0;
  				len = m_numEndPoints + 1;
  			}
  			else
  			{
  				endPoint = 1;
  				len = m_numEndPoints;
  			}
  
  			// Create all the command classes for all the endpoints
  			for( uint8 i = 1; i <= len; i++ )
  			{
  				//std::cout << "Num Instances: " << len << std::endl;
  				for( set<uint8>::iterator it = m_endPointCommandClasses.begin(); it != m_endPointCommandClasses.end(); ++it )
  				{
  					uint8 commandClassId = *it;
  					CommandClass* cc = node->GetCommandClass( commandClassId );
  					if( cc )
  					{
  						cc->SetInstance( i );
  						if( m_endPointMap != MultiInstanceMapAll || i != 1 )
  						{
  							cc->SetEndPoint( i, endPoint );
  						}
  						// If we support the BASIC command class and it is mapped to a command class
  						// assigned to this end point, make sure the BASIC command class is also associated
  						// with this end point.
  						if( basic != NULL && basic->GetMapping() == commandClassId )
  						{
  							basic->SetInstance( i );
  							if( m_endPointMap != MultiInstanceMapAll || i != 1 )
  							{
  								basic->SetEndPoint( i, endPoint );
  							}
  						}
  					}
  				}
  				endPoint++;
  			}
  		}
  		else							// Endpoints are different
  		{
  			for( set<uint8>::iterator it = m_endPointCommandClasses.begin(); it != m_endPointCommandClasses.end(); ++it )
  			{
  				uint8 commandClassId = *it;
  				CommandClass* cc = node->GetCommandClass( commandClassId );
  				if( cc )
  				{
  					uint8 i;
  					// Find the next free instance of this class
  					for( i = 1; i <= 127; i++ )
  					{
  						if( m_endPointMap == MultiInstanceMapAll ) // Include the non-endpoint instance
  						{
  							if( !cc->GetInstances()->IsSet( i ) )
  							{
  								break;
  							}
  						}
  						// Reuse non-endpoint instances first time we see it
  						else if( i == 1 && cc->GetInstances()->IsSet( i ) && cc->GetEndPoint( i ) == 0 )
  						{
  							break;
  						}
  						// Find the next free instance
  						else if( !cc->GetInstances()->IsSet( i ) )
  						{
  							break;
  						}
  					}
  					cc->SetInstance( i );
  					cc->SetEndPoint( i, endPoint );
  					// If we support the BASIC command class and it is mapped to a command class
  					// assigned to this end point, make sure the BASIC command class is also associated
  					// with this end point.
  					if( basic != NULL && basic->GetMapping() == commandClassId )
  					{
  						basic->SetInstance( i );
  						basic->SetEndPoint( i, endPoint );
  					}
  				}
  			}
  		}
  	}
  }
  
  //-----------------------------------------------------------------------------
  // <MultiInstance::HandleMultiChannelEndPointFindReport>
  // Handle a message from the Z-Wave network
  //-----------------------------------------------------------------------------
  void MultiInstance::HandleMultiChannelEndPointFindReport
  (
  		uint8 const* _data,
  		uint32 const _length
  )
  {
  	Log::Write( LogLevel_Info, GetNodeId(), "Received MultiChannelEndPointFindReport from node %d", GetNodeId() );
  	uint8 numEndPoints = _length - 5;
  	for( uint8 i=0; i<numEndPoints; ++i )
  	{
  		uint8 endPoint = _data[i+4] & 0x7f;
  
  		if( m_endPointsAreSameClass )
  		{
  			// Use the stored command class list to set up the endpoint.
  			if( Node* node = GetNodeUnsafe() )
  			{
  				for( set<uint8>::iterator it=m_endPointCommandClasses.begin(); it!=m_endPointCommandClasses.end(); ++it )
  				{
  					uint8 commandClassId = *it;
  					CommandClass* cc = node->GetCommandClass( commandClassId );
  					if( cc )
  					{
  						Log::Write( LogLevel_Info, GetNodeId(), "    Endpoint %d: Adding %s", endPoint, cc->GetCommandClassName().c_str() );
  						cc->SetInstance( endPoint );
  					}
  				}
  			}
  		}
  		else
  		{
  			// Endpoints are different, so request the capabilities
  			Log::Write(LogLevel_Info, GetNodeId(), "MultiChannelCmd_CapabilityGet for node %d, endpoint %d", GetNodeId(), endPoint );
  			Msg* msg = new Msg( "MultiChannelCmd_CapabilityGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
  			msg->Append( GetNodeId() );
  			msg->Append( 3 );
  			msg->Append( GetCommandClassId() );
  			msg->Append( MultiChannelCmd_CapabilityGet );
  			msg->Append( endPoint );
  			msg->Append( GetDriver()->GetTransmitOptions() );
  			GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
  		}
  	}
  
  	m_numEndPointsFound += numEndPoints;
  	if( !m_endPointsAreSameClass )
  	{
  		if( _data[1] == 0 )
  		{
  			// No more reports to follow this one, so we can continue the search.
  			if( m_numEndPointsFound < numEndPoints )
  			{
  				// We have not yet found all the endpoints, so move to the next generic class request
  				++m_endPointFindIndex;
  				if (m_endPointFindIndex <= 13) /* we are finished */
  				{
  					if( c_genericClass[m_endPointFindIndex] > 0 )
  					{
  						if (m_endPointFindIndex > 13) /* size of c_genericClassName minus Unknown Entry */
  						{
  							Log::Write (LogLevel_Warning, GetNodeId(), "m_endPointFindIndex Value was greater than range. Setting to Unknown");
  							m_endPointFindIndex = 14;
  						}
  
  						Log::Write(LogLevel_Info, GetNodeId(), "MultiChannelCmd_EndPointFind for generic device class 0x%.2x (%s)", c_genericClass[m_endPointFindIndex], c_genericClassName[m_endPointFindIndex] );
  						Msg* msg = new Msg( "MultiChannelCmd_EndPointFind", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
  						msg->Append( GetNodeId() );
  						msg->Append( 4 );
  						msg->Append( GetCommandClassId() );
  						msg->Append( MultiChannelCmd_EndPointFind );
  						msg->Append( c_genericClass[m_endPointFindIndex] );		// Generic device class
  						msg->Append( 0xff );									// Any specific device class
  						msg->Append( GetDriver()->GetTransmitOptions() );
  						GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
  					}
  				} else {
  					Log::Write (LogLevel_Warning, GetNodeId(), "m_endPointFindIndex is higher than range. Not Sending MultiChannelCmd_EndPointFind message");
  				}
  			}
  		}
  	}
  }
  
  //-----------------------------------------------------------------------------
  // <MultiInstance::HandleMultiChannelEncap>
  // Handle a message from the Z-Wave network
  //-----------------------------------------------------------------------------
  void MultiInstance::HandleMultiChannelEncap
  (
  		uint8 const* _data,
  		uint32 const _length
  )
  {
  	if( Node* node = GetNodeUnsafe() )
  	{
  		uint8 endPoint = _data[1] & 0x7f;
  		uint8 commandClassId = _data[3];
  		if( CommandClass* pCommandClass = node->GetCommandClass( commandClassId ) )
  		{
  			uint8 instance = pCommandClass->GetInstance( endPoint );
  			if( instance == 0 )
  			{
  				Log::Write( LogLevel_Error, GetNodeId(), "Cannot find endpoint map to instance for Command Class %s endpoint %d", pCommandClass->GetCommandClassName().c_str(), endPoint );
  			}
  			else
  			{
  				Log::Write( LogLevel_Info, GetNodeId(), "Received a MultiChannelEncap from node %d, endpoint %d for Command Class %s", GetNodeId(), endPoint, pCommandClass->GetCommandClassName().c_str() );
  				pCommandClass->HandleMsg( &_data[4], _length-4, instance );
  			}
  		} else {
  			Log::Write(LogLevel_Error, GetNodeId(), "Received a MultiChannelEncap for endpoint %d for Command Class %d, which we can't find", endPoint, commandClassId);
  		}
  	}
  }
  