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