1 //-----------------------------------------------------------------------------
2 //
3 //	CommandClass.cpp
4 //
5 //	Base class for all Z-Wave Command Classes
6 //
7 //	Copyright (c) 2010 Mal Lansell <openzwave@lansell.org>
8 //
9 //	SOFTWARE NOTICE AND LICENSE
10 //
11 //	This file is part of OpenZWave.
12 //
13 //	OpenZWave is free software: you can redistribute it and/or modify
14 //	it under the terms of the GNU Lesser General Public License as published
15 //	by the Free Software Foundation, either version 3 of the License,
16 //	or (at your option) any later version.
17 //
18 //	OpenZWave is distributed in the hope that it will be useful,
19 //	but WITHOUT ANY WARRANTY; without even the implied warranty of
20 //	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 //	GNU Lesser General Public License for more details.
22 //
23 //	You should have received a copy of the GNU Lesser General Public License
24 //	along with OpenZWave.  If not, see <http://www.gnu.org/licenses/>.
25 //
26 //-----------------------------------------------------------------------------
27 
28 #include <math.h>
29 #include <locale.h>
30 #include "tinyxml.h"
31 #include "command_classes/CommandClass.h"
32 #include "command_classes/Basic.h"
33 #include "command_classes/MultiInstance.h"
34 #include "command_classes/CommandClasses.h"
35 #include "Msg.h"
36 #include "Node.h"
37 #include "Driver.h"
38 #include "Manager.h"
39 #include "platform/Log.h"
40 #include "value_classes/ValueStore.h"
41 
42 using namespace OpenZWave;
43 
44 static uint8 const	c_sizeMask		= 0x07;
45 static uint8 const	c_scaleMask		= 0x18;
46 static uint8 const	c_scaleShift		= 0x03;
47 static uint8 const	c_precisionMask		= 0xe0;
48 static uint8 const	c_precisionShift	= 0x05;
49 
50 //-----------------------------------------------------------------------------
51 // <CommandClass::CommandClass>
52 // Constructor
53 //-----------------------------------------------------------------------------
CommandClass(uint32 const _homeId,uint8 const _nodeId)54 CommandClass::CommandClass
55 (
56 		uint32 const _homeId,
57 		uint8 const _nodeId
58 ):
59 m_homeId( _homeId ),
60 m_nodeId( _nodeId ),
61 m_version( 1 ),
62 m_afterMark( false ),
63 m_createVars( true ),
64 m_overridePrecision( -1 ),
65 m_getSupported( true ),
66 m_isSecured( false ),
67 m_SecureSupport( true ),
68 m_inNIF(false),
69 m_staticRequests( 0 ),
70 m_sentCnt( 0 ),
71 m_receivedCnt( 0 )
72 {
73 }
74 
75 //-----------------------------------------------------------------------------
76 // <CommandClass::~CommandClass>
77 // Destructor
78 //-----------------------------------------------------------------------------
~CommandClass()79 CommandClass::~CommandClass
80 (
81 )
82 {
83 	while( !m_endPointMap.empty() )
84 	{
85 		map<uint8,uint8>::iterator it = m_endPointMap.begin();
86 		m_endPointMap.erase( it );
87 	}
88 	while ( !m_RefreshClassValues.empty() )
89 	{
90 		for (unsigned int i = 0; i < m_RefreshClassValues.size(); i++)
91 		{
92 			RefreshValue *rcc = m_RefreshClassValues.at(i);
93 			while(!rcc->RefreshClasses.empty()) {
94 				delete rcc->RefreshClasses.back();
95 				rcc->RefreshClasses.pop_back();
96 			}
97 			//			for (unsigned int j = 0; j < rcc->RefreshClasses.size(); i++)
98 			//			{
99 			//				delete rcc->RefreshClasses[j];
100 			//			}
101 			rcc->RefreshClasses.clear();
102 			delete rcc;
103 		}
104 		m_RefreshClassValues.clear();
105 	}
106 
107 }
108 
109 //-----------------------------------------------------------------------------
110 // <CommandClass::GetDriver>
111 // Get a pointer to our driver
112 //-----------------------------------------------------------------------------
GetDriver() const113 Driver* CommandClass::GetDriver
114 (
115 )const
116 {
117 	return( Manager::Get()->GetDriver( m_homeId ) );
118 }
119 
120 //-----------------------------------------------------------------------------
121 // <CommandClass::GetNode>
122 // Get a pointer to our node without locking the mutex
123 //-----------------------------------------------------------------------------
GetNodeUnsafe() const124 Node* CommandClass::GetNodeUnsafe
125 (
126 )const
127 {
128 	return( GetDriver()->GetNodeUnsafe( m_nodeId ) );
129 }
130 
131 //-----------------------------------------------------------------------------
132 // <CommandClass::GetValue>
133 // Get a pointer to a value by its instance and index
134 //-----------------------------------------------------------------------------
GetValue(uint8 const _instance,uint8 const _index)135 Value* CommandClass::GetValue
136 (
137 		uint8 const _instance,
138 		uint8 const _index
139 )
140 {
141 	Value* value = NULL;
142 	if( Node* node = GetNodeUnsafe() )
143 	{
144 		value = node->GetValue( GetCommandClassId(), _instance, _index );
145 	}
146 	return value;
147 }
148 
149 //-----------------------------------------------------------------------------
150 // <CommandClass::RemoveValue>
151 // Remove a value by its instance and index
152 //-----------------------------------------------------------------------------
RemoveValue(uint8 const _instance,uint8 const _index)153 bool CommandClass::RemoveValue
154 (
155 		uint8 const _instance,
156 		uint8 const _index
157 )
158 {
159 	if( Node* node = GetNodeUnsafe() )
160 	{
161 		return node->RemoveValue( GetCommandClassId(), _instance, _index );
162 	}
163 	return false;
164 }
165 
166 //-----------------------------------------------------------------------------
167 // <CommandClass::SetInstances>
168 // Instances as set by the MultiInstance V1 command class
169 //-----------------------------------------------------------------------------
SetInstances(uint8 const _instances)170 void CommandClass::SetInstances
171 (
172 		uint8 const _instances
173 )
174 {
175 	// Ensure we have a set of reported variables for each new instance
176 	if( !m_afterMark )
177 	{
178 		for( uint8 i=0; i<_instances; ++i )
179 		{
180 			SetInstance( i+1 );
181 		}
182 	}
183 }
184 
185 //-----------------------------------------------------------------------------
186 // <CommandClass::SetInstance>
187 // Instances as set by the MultiChannel (i.e. MultiInstance V2) command class
188 //-----------------------------------------------------------------------------
SetInstance(uint8 const _endPoint)189 void CommandClass::SetInstance
190 (
191 		uint8 const _endPoint
192 )
193 {
194 	if( !m_instances.IsSet( _endPoint ) )
195 	{
196 		m_instances.Set( _endPoint );
197 		if( IsCreateVars() )
198 		{
199 			CreateVars( _endPoint );
200 		}
201 	}
202 }
203 
204 //-----------------------------------------------------------------------------
205 // <CommandClass::ReadXML>
206 // Read the saved command class data
207 //-----------------------------------------------------------------------------
ReadXML(TiXmlElement const * _ccElement)208 void CommandClass::ReadXML
209 (
210 		TiXmlElement const* _ccElement
211 )
212 {
213 	int32 intVal;
214 	char const* str;
215 
216 	if( TIXML_SUCCESS == _ccElement->QueryIntAttribute( "version", &intVal ) )
217 	{
218 		m_version = (uint8)intVal;
219 	}
220 
221 	uint8 instances = 1;
222 	if( TIXML_SUCCESS == _ccElement->QueryIntAttribute( "instances", &intVal ) )
223 	{
224 		instances = (uint8)intVal;
225 	}
226 
227 	if( TIXML_SUCCESS == _ccElement->QueryIntAttribute( "request_flags", &intVal ) )
228 	{
229 		m_staticRequests = (uint8)intVal;
230 	}
231 
232 	if( TIXML_SUCCESS == _ccElement->QueryIntAttribute( "override_precision", &intVal ) )
233 	{
234 		m_overridePrecision = (int8)intVal;
235 	}
236 
237 	str = _ccElement->Attribute( "after_mark" );
238 	if( str )
239 	{
240 		m_afterMark = !strcmp( str, "true" );
241 	}
242 
243 	str = _ccElement->Attribute( "create_vars" );
244 	if( str )
245 	{
246 		m_createVars = !strcmp( str, "true" );
247 	}
248 
249 	// Make sure previously created values are removed if create_vars=false
250 	if( !m_createVars )
251 	{
252 		if( Node* node = GetNodeUnsafe() )
253 		{
254 			node->GetValueStore()->RemoveCommandClassValues( GetCommandClassId() );
255 		}
256 	}
257 
258 	str = _ccElement->Attribute( "getsupported" );
259 	if( str )
260 	{
261 		m_getSupported = !strcmp( str, "true" );
262 	}
263 
264 	str = _ccElement->Attribute( "issecured" );
265 	if( str )
266 	{
267 		m_isSecured = !strcmp( str, "true" );
268 	}
269 	str = _ccElement->Attribute( "innif" );
270 	if( str )
271 	{
272 		m_inNIF = !strcmp( str, "true" );
273 	}
274 
275 
276 	// Setting the instance count will create all the values.
277 	SetInstances( instances );
278 
279 	// Apply any differences from the saved XML to the values
280 	TiXmlElement const* child = _ccElement->FirstChildElement();
281 	while( child )
282 	{
283 		str = child->Value();
284 		if( str )
285 		{
286 			if( !strcmp( str, "Instance" ) )
287 			{
288 				uint8 instance = 0;
289 				// Add an instance to the command class
290 				if( TIXML_SUCCESS == child->QueryIntAttribute( "index", &intVal ) )
291 				{
292 					instance = (uint8)intVal;
293 					SetInstance( instance );
294 				}
295 				// See if its associated endpoint is present
296 				if( TIXML_SUCCESS == child->QueryIntAttribute( "endpoint", &intVal ) )
297 				{
298 					uint8 endpoint = (uint8)intVal;
299 					SetEndPoint( instance, endpoint );
300 				}
301 			}
302 			else if( !strcmp( str, "Value" ) )
303 			{
304 				// Apply any differences from the saved XML to the value
305 				GetNodeUnsafe()->ReadValueFromXML( GetCommandClassId(), child );
306 			}
307 			else if (!strcmp( str, "TriggerRefreshValue" ) )
308 			{
309 				ReadValueRefreshXML(child);
310 			}
311 		}
312 
313 		child = child->NextSiblingElement();
314 	}
315 }
316 
317 //-----------------------------------------------------------------------------
318 // <CommandClass::ReadValueRefreshXML>
319 // Read the config that contains a list of Values that should be refreshed when
320 // we recieve u updated Value from a device. (This helps Yale Door Locks, which send a
321 // Alarm Report instead of DoorLock Report when the status of the Door Lock is changed
322 //-----------------------------------------------------------------------------
ReadValueRefreshXML(TiXmlElement const * _ccElement)323 void CommandClass::ReadValueRefreshXML
324 (
325 		TiXmlElement const* _ccElement
326 )
327 {
328 
329 	char const* str;
330 	bool ok = false;
331 	const char *genre;
332 	RefreshValue *rcc = new RefreshValue();
333 	rcc->cc = GetCommandClassId();
334 	genre = _ccElement->Attribute( "Genre" );
335 	rcc->genre = Value::GetGenreEnumFromName(genre);
336 	int temp;
337 	_ccElement->QueryIntAttribute( "Instance", &temp);
338 	rcc->instance = (uint8)temp;
339 	_ccElement->QueryIntAttribute( "Index", &temp);
340 	rcc->index = (uint8)temp;
341 	Log::Write(LogLevel_Info, GetNodeId(), "Value Refresh triggered by CommandClass: %s, Genre: %d, Instance: %d, Index: %d for:", GetCommandClassName().c_str(), rcc->genre, rcc->instance, rcc->index);
342 	TiXmlElement const* child = _ccElement->FirstChildElement();
343 	while( child )
344 	{
345 		str = child->Value();
346 		if( str )
347 		{
348 			if ( !strcmp(str, "RefreshClassValue"))
349 			{
350 				RefreshValue *arcc = new RefreshValue();
351 				if (child->QueryIntAttribute( "CommandClass", &temp) != TIXML_SUCCESS) {
352 					Log::Write(LogLevel_Warning, GetNodeId(), "    Invalid XML - CommandClass Attribute is wrong type or missing");
353 					child = child->NextSiblingElement();
354 					continue;
355 				}
356 				arcc->cc = (uint8)temp;
357 				if (child->QueryIntAttribute( "RequestFlags", &temp) != TIXML_SUCCESS) {
358 					Log::Write(LogLevel_Warning, GetNodeId(), "    Invalid XML - RequestFlags Attribute is wrong type or missing");
359 					child = child->NextSiblingElement();
360 					continue;
361 				}
362 				arcc->genre = (uint8)temp;
363 				if (child->QueryIntAttribute( "Instance", &temp) != TIXML_SUCCESS) {
364 					Log::Write(LogLevel_Warning, GetNodeId(), "    Invalid XML - Instance Attribute is wrong type or missing");
365 					child = child->NextSiblingElement();
366 					continue;
367 				}
368 				arcc->instance = (uint8)temp;
369 				if (child->QueryIntAttribute( "Index", &temp) != TIXML_SUCCESS) {
370 					Log::Write(LogLevel_Warning, GetNodeId(), "    Invalid XML - Index Attribute is wrong type or missing");
371 					child = child->NextSiblingElement();
372 					continue;
373 				}
374 				arcc->index = (uint8)temp;
375 				Log::Write(LogLevel_Info, GetNodeId(), "    CommandClass: %s, RequestFlags: %d, Instance: %d, Index: %d", CommandClasses::GetName(arcc->cc).c_str(), arcc->genre, arcc->instance, arcc->index);
376 				rcc->RefreshClasses.push_back(arcc);
377 				ok = true;
378 			}
379 			else
380 			{
381 				Log::Write(LogLevel_Warning, GetNodeId(), "Got Unhandled Child Entry in TriggerRefreshValue XML Config: %s", str);
382 			}
383 		}
384 		child = child->NextSiblingElement();
385 	}
386 	if (ok == true) {
387 		m_RefreshClassValues.push_back( rcc );
388 	} else {
389 		Log::Write(LogLevel_Warning, GetNodeId(), "Failed to add a RefreshClassValue from XML");
390 		delete rcc;
391 	}
392 }
393 
394 //-----------------------------------------------------------------------------
395 // <CommandClass::CheckForRefreshValues>
396 // Scan our m_RefreshClassValues vector to see if we should call any other
397 // Command Classes to refresh their value
398 //-----------------------------------------------------------------------------
399 
CheckForRefreshValues(Value const * _value)400 bool CommandClass::CheckForRefreshValues (
401 		Value const* _value
402 )
403 {
404 	if (m_RefreshClassValues.empty())
405 	{
406 		//Log::Write(LogLevel_Debug, GetNodeId(), "Bailing out of CheckForRefreshValues");
407 		return false;
408 	}
409 	Node* node = GetNodeUnsafe();
410 	if( node != NULL )
411 	{
412 		for (uint32 i = 0; i < m_RefreshClassValues.size(); i++)
413 		{
414 			RefreshValue *rcc = m_RefreshClassValues.at(i);
415 			//Log::Write(LogLevel_Debug, GetNodeId(), "Checking Value Against RefreshClassList: CommandClass %s = %s, Genre %d = %d, Instance %d = %d, Index %d = %d", CommandClasses::GetName(rcc->cc).c_str(), CommandClasses::GetName(_value->GetID().GetCommandClassId()).c_str(), rcc->genre, _value->GetID().GetGenre(), rcc->instance, _value->GetID().GetInstance(), rcc->index, _value->GetID().GetIndex());
416 			if ((rcc->genre == _value->GetID().GetGenre()) && (rcc->instance == _value->GetID().GetInstance()) && (rcc->index == _value->GetID().GetIndex()) )
417 			{
418 				/* we got a match..... */
419 				for (uint32 j = 0; j < rcc->RefreshClasses.size(); j++)
420 				{
421 					RefreshValue *arcc = rcc->RefreshClasses.at(j);
422 					Log::Write(LogLevel_Debug, GetNodeId(), "Requesting Refresh of Value: CommandClass: %s Genre %d, Instance %d, Index %d", CommandClasses::GetName(arcc->cc).c_str(), arcc->genre, arcc->instance, arcc->index);
423 					if( CommandClass* cc = node->GetCommandClass( arcc->cc ) )
424 					{
425 						cc->RequestValue(arcc->genre, arcc->index, arcc->instance, Driver::MsgQueue_Send);
426 					}
427 				}
428 			}
429 		}
430 	}
431 	else /* Driver */
432 	{
433 		Log::Write(LogLevel_Warning, GetNodeId(), "Can't get Node");
434 	}
435 	return true;
436 }
437 
438 //-----------------------------------------------------------------------------
439 // <CommandClass::WriteXML>
440 // Save the static node configuration data
441 //-----------------------------------------------------------------------------
WriteXML(TiXmlElement * _ccElement)442 void CommandClass::WriteXML
443 (
444 		TiXmlElement* _ccElement
445 )
446 {
447 	char str[32];
448 
449 	snprintf( str, sizeof(str), "%d", GetCommandClassId() );
450 	_ccElement->SetAttribute( "id", str );
451 	_ccElement->SetAttribute( "name", GetCommandClassName().c_str() );
452 
453 	snprintf( str, sizeof(str), "%d", GetVersion() );
454 	_ccElement->SetAttribute( "version", str );
455 
456 	if( m_staticRequests )
457 	{
458 		snprintf( str, sizeof(str), "%d", m_staticRequests );
459 		_ccElement->SetAttribute( "request_flags", str );
460 	}
461 
462 	if( m_overridePrecision >= 0 )
463 	{
464 		snprintf( str, sizeof(str), "%d", m_overridePrecision );
465 		_ccElement->SetAttribute( "override_precision", str );
466 	}
467 
468 	if( m_afterMark )
469 	{
470 		_ccElement->SetAttribute( "after_mark", "true" );
471 	}
472 
473 	if( !m_createVars )
474 	{
475 		_ccElement->SetAttribute( "create_vars", "false" );
476 	}
477 
478 	if( !m_getSupported )
479 	{
480 		_ccElement->SetAttribute( "getsupported", "false" );
481 	}
482 	if ( m_isSecured )
483 	{
484 		_ccElement->SetAttribute( "issecured", "true" );
485 	}
486 	if ( m_inNIF )
487 	{
488 		_ccElement->SetAttribute( "innif", "true" );
489 	}
490 
491 
492 	// Write out the instances
493 	for( Bitfield::Iterator it = m_instances.Begin(); it != m_instances.End(); ++ it )
494 	{
495 		TiXmlElement* instanceElement = new TiXmlElement( "Instance" );
496 		_ccElement->LinkEndChild( instanceElement );
497 
498 		snprintf( str, sizeof(str), "%d", *it );
499 		instanceElement->SetAttribute( "index", str );
500 
501 		map<uint8,uint8>::iterator eit = m_endPointMap.find( *it );
502 		if( eit != m_endPointMap.end() )
503 		{
504 			snprintf( str, sizeof(str), "%d", eit->second );
505 			instanceElement->SetAttribute( "endpoint", str );
506 		}
507 	}
508 
509 	// Write out the values for this command class
510 	ValueStore* store = GetNodeUnsafe()->GetValueStore();
511 	for( ValueStore::Iterator it = store->Begin(); it != store->End(); ++it )
512 	{
513 		Value* value = it->second;
514 		if( value->GetID().GetCommandClassId() == GetCommandClassId() )
515 		{
516 			TiXmlElement* valueElement = new TiXmlElement( "Value" );
517 			_ccElement->LinkEndChild( valueElement );
518 			value->WriteXML( valueElement );
519 		}
520 	}
521 	// Write out the TriggerRefreshValue if it exists
522 	for (uint32 i = 0; i < m_RefreshClassValues.size(); i++)
523 	{
524 		RefreshValue *rcc = m_RefreshClassValues.at(i);
525 		TiXmlElement* RefreshElement = new TiXmlElement("TriggerRefreshValue");
526 		_ccElement->LinkEndChild( RefreshElement );
527 		RefreshElement->SetAttribute("Genre", Value::GetGenreNameFromEnum((ValueID::ValueGenre)rcc->genre));
528 		RefreshElement->SetAttribute("Instance", rcc->instance);
529 		RefreshElement->SetAttribute("Index", rcc->index);
530 		for (uint32 j = 0; j < rcc->RefreshClasses.size(); j++)
531 		{
532 			RefreshValue *arcc = rcc->RefreshClasses.at(j);
533 			TiXmlElement *ClassElement = new TiXmlElement("RefreshClassValue");
534 			RefreshElement->LinkEndChild(ClassElement);
535 			ClassElement->SetAttribute("CommandClass", arcc->cc);
536 			ClassElement->SetAttribute("RequestFlags", arcc->genre);
537 			ClassElement->SetAttribute("Instance", arcc->instance);
538 			ClassElement->SetAttribute("Index", arcc->index);
539 		}
540 	}
541 }
542 
543 //-----------------------------------------------------------------------------
544 // <CommandClass::ExtractValue>
545 // Read a value from a variable length sequence of bytes
546 //-----------------------------------------------------------------------------
ExtractValue(uint8 const * _data,uint8 * _scale,uint8 * _precision,uint8 _valueOffset) const547 string CommandClass::ExtractValue
548 (
549 		uint8 const* _data,
550 		uint8* _scale,
551 		uint8* _precision,
552 		uint8 _valueOffset // = 1
553 )const
554 {
555 	uint8 const size = _data[0] & c_sizeMask;
556 	uint8 const precision = (_data[0] & c_precisionMask) >> c_precisionShift;
557 
558 	if( _scale )
559 	{
560 		*_scale = (_data[0] & c_scaleMask) >> c_scaleShift;
561 	}
562 
563 	if( _precision )
564 	{
565 		*_precision = precision;
566 	}
567 
568 	uint32 value = 0;
569 	uint8 i;
570 	for( i=0; i<size; ++i )
571 	{
572 		value <<= 8;
573 		value |= (uint32)_data[i+(uint32)_valueOffset];
574 	}
575 
576 	// Deal with sign extension.  All values are signed
577 	string res;
578 	if( _data[_valueOffset] & 0x80 )
579 	{
580 		res = "-";
581 
582 		// MSB is signed
583 		if( size == 1 )
584 		{
585 			value |= 0xffffff00;
586 		}
587 		else if( size == 2 )
588 		{
589 			value |= 0xffff0000;
590 		}
591 	}
592 
593 	// Convert the integer to a decimal string.  We avoid
594 	// using floats to prevent accuracy issues.
595 	char numBuf[12] = {0};
596 
597 	if( precision == 0 )
598 	{
599 		// The precision is zero, so we can just print the number directly into the string.
600 		snprintf( numBuf, 12, "%d", (signed int)value );
601 		res = numBuf;
602 	}
603 	else
604 	{
605 		// We'll need to insert a decimal point and include any necessary leading zeros.
606 
607 		// Fill the buffer with the value padded with leading zeros.
608 		snprintf( numBuf, 12, "%011d", (signed int)value );
609 
610 		// Calculate the position of the decimal point in the buffer
611 		int32 decimal = 10-precision;
612 
613 		// Shift the characters to make space for the decimal point.
614 		// We don't worry about overwriting any minus sign since that is
615 		// already written into the res string. While we're shifting, we
616 		// also look for the real starting position of the number so we
617 		// can copy it into the res string later.
618 		int32 start = -1;
619 		for( int32 i=0; i<decimal; ++i )
620 		{
621 			numBuf[i] = numBuf[i+1];
622 			if( ( start<0 ) && ( numBuf[i] != '0' ) )
623 			{
624 				start = i;
625 			}
626 		}
627 		if( start<0 )
628 		{
629 			start = decimal-1;
630 		}
631 
632 		// Insert the decimal point
633 		struct lconv const* locale = localeconv();
634 		numBuf[decimal] = *(locale->decimal_point);
635 
636 		// Copy the buffer into res
637 		res += &numBuf[start];
638 	}
639 
640 	return res;
641 }
642 
643 //-----------------------------------------------------------------------------
644 // <CommandClass::AppendValue>
645 // Add a value to a message as a sequence of bytes
646 //-----------------------------------------------------------------------------
AppendValue(Msg * _msg,string const & _value,uint8 const _scale) const647 void CommandClass::AppendValue
648 (
649 		Msg* _msg,
650 		string const& _value,
651 		uint8 const _scale
652 )const
653 {
654 	uint8 precision;
655 	uint8 size;
656 	int32 val = ValueToInteger( _value, &precision, &size );
657 
658 	_msg->Append( (precision<<c_precisionShift) | (_scale<<c_scaleShift) | size );
659 
660 	int32 shift = (size-1)<<3;
661 	for( int32 i=size; i>0; --i, shift-=8 )
662 	{
663 		_msg->Append( (uint8)(val >> shift) );
664 	}
665 }
666 
667 //-----------------------------------------------------------------------------
668 // <CommandClass::GetAppendValueSize>
669 // Get the number of bytes that would be added by a call to AppendValue
670 //-----------------------------------------------------------------------------
GetAppendValueSize(string const & _value) const671 uint8 const CommandClass::GetAppendValueSize
672 (
673 		string const& _value
674 )const
675 {
676 	uint8 size;
677 	ValueToInteger( _value, NULL, &size );
678 	return size;
679 }
680 
681 //-----------------------------------------------------------------------------
682 // <CommandClass::ValueToInteger>
683 // Convert a decimal string to an integer and report the precision and
684 // number of bytes required to store the value.
685 //-----------------------------------------------------------------------------
ValueToInteger(string const & _value,uint8 * o_precision,uint8 * o_size) const686 int32 CommandClass::ValueToInteger
687 (
688 		string const& _value,
689 		uint8* o_precision,
690 		uint8* o_size
691 )const
692 {
693 	int32 val;
694 	uint8 precision;
695 
696 	// Find the decimal point
697 	size_t pos = _value.find_first_of( "." );
698 	if( pos == string::npos )
699 		pos = _value.find_first_of( "," );
700 
701 	if( pos == string::npos )
702 	{
703 		// No decimal point
704 		precision = 0;
705 
706 		// Convert the string to an integer
707 		val = atol( _value.c_str() );
708 	}
709 	else
710 	{
711 		// Remove the decimal point and convert to an integer
712 		precision = (uint8) ((_value.size()-pos)-1);
713 
714 		string str = _value.substr( 0, pos ) + _value.substr( pos+1 );
715 		val = atol( str.c_str() );
716 	}
717 
718 	if ( m_overridePrecision > 0 )
719 	{
720 		while ( precision < m_overridePrecision ) {
721 			precision++;
722 			val *= 10;
723 		}
724 	}
725 
726 	if ( o_precision ) *o_precision = precision;
727 
728 	if( o_size )
729 	{
730 		// Work out the size as either 1, 2 or 4 bytes
731 		*o_size = 4;
732 		if( val < 0 )
733 		{
734 			if( ( val & 0xffffff80 ) == 0xffffff80 )
735 			{
736 				*o_size = 1;
737 			}
738 			else if( ( val & 0xffff8000 ) == 0xffff8000 )
739 			{
740 				*o_size = 2;
741 			}
742 		}
743 		else
744 		{
745 			if( ( val & 0xffffff00 ) == 0 )
746 			{
747 				*o_size = 1;
748 			}
749 			else if( ( val & 0xffff0000 ) == 0 )
750 			{
751 				*o_size = 2;
752 			}
753 		}
754 	}
755 
756 	return val;
757 }
758 
759 //-----------------------------------------------------------------------------
760 // <CommandClass::UpdateMappedClass>
761 // Update the mapped class if there is one with BASIC level
762 //-----------------------------------------------------------------------------
UpdateMappedClass(uint8 const _instance,uint8 const _classId,uint8 const _level)763 void CommandClass::UpdateMappedClass
764 (
765 		uint8 const _instance,
766 		uint8 const _classId,
767 		uint8 const _level
768 )
769 {
770 	if( _classId )
771 	{
772 		if( Node* node = GetNodeUnsafe() )
773 		{
774 			CommandClass* cc = node->GetCommandClass( _classId );
775 			if( node->GetCurrentQueryStage() == Node::QueryStage_Complete && cc != NULL )
776 			{
777 				cc->SetValueBasic( _instance, _level );
778 			}
779 		}
780 	}
781 }
782 
783 //-----------------------------------------------------------------------------
784 // <CommandClass::ClearStaticRequest>
785 // The static data for this command class has been read from the device
786 //-----------------------------------------------------------------------------
ClearStaticRequest(uint8 _request)787 void CommandClass::ClearStaticRequest
788 (
789 		uint8 _request
790 )
791 {
792 	m_staticRequests &= ~_request;
793 }
794 
795 //-----------------------------------------------------------------------------
796 // <CommandClass::RequestStateForAllInstances>
797 // Request current state from the device
798 //-----------------------------------------------------------------------------
RequestStateForAllInstances(uint32 const _requestFlags,Driver::MsgQueue const _queue)799 bool CommandClass::RequestStateForAllInstances
800 (
801 		uint32 const _requestFlags,
802 		Driver::MsgQueue const _queue
803 )
804 {
805 	bool res = false;
806 	if( m_createVars )
807 	{
808 		if( Node* node = GetNodeUnsafe() )
809 		{
810 			MultiInstance* multiInstance = static_cast<MultiInstance*>( node->GetCommandClass( MultiInstance::StaticGetCommandClassId() ) );
811 			if( multiInstance != NULL )
812 			{
813 				for( Bitfield::Iterator it = m_instances.Begin(); it != m_instances.End(); ++it )
814 				{
815 					res |= RequestState( _requestFlags, (uint8)*it, _queue );
816 				}
817 			}
818 			else
819 			{
820 				res = RequestState( _requestFlags, 1, _queue );
821 			}
822 		}
823 	}
824 
825 	return res;
826 }
827 
828 
829