//----------------------------------------------------------------------------- // // Node.h // // A node in the Z-Wave network // // Copyright (c) 2010 Mal Lansell // // 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 . // //----------------------------------------------------------------------------- #ifndef _Node_H #define _Node_H #include #include #include #include #include "Defs.h" #include "value_classes/ValueID.h" #include "value_classes/ValueList.h" #include "Msg.h" #include "platform/TimeStamp.h" class TiXmlElement; namespace OpenZWave { class CommandClass; class Driver; class Group; class ValueStore; class Value; class ValueBool; class ValueButton; class ValueByte; class ValueDecimal; class ValueInt; class ValueSchedule; class ValueShort; class ValueString; class Mutex; /** \brief The Node class describes a Z-Wave node object...typically a device on the * Z-Wave network. */ class Node { friend class Manager; friend class Driver; friend class Group; friend class Value; friend class ValueButton; friend class Alarm; friend class Association; friend class AssociationCommandConfiguration; friend class Basic; friend class Battery; friend class ClimateControlSchedule; friend class Clock; friend class CommandClass; friend class ControllerReplication; friend class EnergyProduction; friend class Hail; friend class Indicator; friend class Language; friend class Lock; friend class ManufacturerSpecific; friend class Meter; friend class MeterPulse; friend class MultiInstance; friend class NodeNaming; friend class Protection; friend class Security; friend class SensorAlarm; friend class SensorBinary; friend class SensorMultilevel; friend class SwitchAll; friend class SwitchBinary; friend class SwitchMultilevel; friend class SwitchToggleBinary; friend class SwitchToggleMultilevel; friend class ThermostatFanMode; friend class ThermostatFanState; friend class ThermostatMode; friend class ThermostatOperatingState; friend class ThermostatSetpoint; friend class Version; friend class WakeUp; //----------------------------------------------------------------------------- // Construction //----------------------------------------------------------------------------- public: /** Constructor initializes the node object, associating it with a specific * network (_homeId) and network node (_nodeId). * \param _homeId The homeId of the network to which this node is connected. * \param _nodeId The nodeId of this node. */ Node( uint32 const _homeId, uint8 const _nodeId ); /** Destructor cleans up memory allocated to node and its child objects. */ virtual ~Node(); private: /** Returns a pointer to the driver (interface with a Z-Wave controller) * associated with this node. */ Driver* GetDriver()const; //----------------------------------------------------------------------------- // Initialization //----------------------------------------------------------------------------- public: enum QueryStage { QueryStage_ProtocolInfo, /**< Retrieve protocol information */ QueryStage_Probe, /**< Ping device to see if alive */ QueryStage_WakeUp, /**< Start wake up process if a sleeping node */ QueryStage_ManufacturerSpecific1, /**< Retrieve manufacturer name and product ids if ProtocolInfo lets us */ QueryStage_NodeInfo, /**< Retrieve info about supported, controlled command classes */ QueryStage_SecurityReport, /**< Retrive a list of Command Classes that require Security */ QueryStage_ManufacturerSpecific2, /**< Retrieve manufacturer name and product ids */ QueryStage_Versions, /**< Retrieve version information */ QueryStage_Instances, /**< Retrieve information about multiple command class instances */ QueryStage_Static, /**< Retrieve static information (doesn't change) */ QueryStage_Probe1, /**< Ping a device upon starting with configuration */ QueryStage_Associations, /**< Retrieve information about associations */ QueryStage_Neighbors, /**< Retrieve node neighbor list */ QueryStage_Session, /**< Retrieve session information (changes infrequently) */ QueryStage_Dynamic, /**< Retrieve dynamic information (changes frequently) */ QueryStage_Configuration, /**< Retrieve configurable parameter information (only done on request) */ QueryStage_Complete, /**< Query process is completed for this node */ QueryStage_None /**< Query process hasn't started for this node */ }; /** * This function advances the query process (see Remarks below for more detail on the * process). It iterates through the various query stages enumerated in Node::QueryStage. * * \remark * For OpenZWave to discover everything about a node, we have to follow a certain * order of queries, because the results of one stage may affect what is requested * in the next stage. The stage is saved with the node data, so that any incomplete * queries can be restarted the next time the application runs. *

* The individual command classes also store some state information as to whether * they have had a response to certain queries. This state information is * initilized by the SetStaticRequests call in QueryStage_None. It is also saved, * so we do not need to request state from every command class if some have previously * responded. */ void AdvanceQueries(); /** * Signal that a specific query stage has been completed for this node. This will * only work if the query process for this node is indeed at the specified stage. * Otherwise, the function returns with no action. * \param _stage The current stage of the query process. */ void QueryStageComplete( QueryStage const _stage ); /** * Retry the specified query stage (up to _maxAttempts retries). This will * only work if the query process for this node is indeed at the specified stage. * Otherwise, the function returns with no action. * \param _stage The query stage to retry. * \param _maxAttempts */ void QueryStageRetry( QueryStage const _stage, uint8 const _maxAttempts = 0 ); // maxAttempts of zero means no limit /** * This function sets the query stage for the node (but only to an earlier stage). * If a later stage is specified than the current one, it is ignored. * \param _stage The desired query stage. * \see m_queryStage, m_queryPending */ void SetQueryStage( QueryStage const _stage, bool const _advance = true ); /** * Returns the current query stage enum. * \return Enum value with the current query stage. * \see m_queryStage */ Node::QueryStage GetCurrentQueryStage() { return m_queryStage; } /** * Returns the specified query stage string. * \param _stage The query stage. * \return Specified query stage string. * \see m_queryStage, m_queryPending */ string GetQueryStageName( QueryStage const _stage ); /** * Returns whether the library thinks a node is functioning properly * \return boolean status of node. */ bool IsNodeAlive()const{ return m_nodeAlive; } /** * This function handles a response to the FUNC_ID_ZW_GET_NODE_PROTOCOL_INFO * command for this node. If protocol information has already been retrieved * for the node, the function simply returns. Otherwise, it populates several * member variables about the device at this node: * - m_routing (whether it is a routing node (capable of passing commands along to other nodes in the network) or not * - m_maxBaudRate (the maximum baud rate at which this device can communicate) * - m_version (TODO) * - m_security (whether device supports security features) * - m_listening (device is powered and listening constantly) * - m_frequentListening (device can be woken up with a beam) * - m_beaming (device is beam capable) */ void UpdateProtocolInfo( uint8 const* _data ); void UpdateNodeInfo( uint8 const* _data, uint8 const _length ); bool ProtocolInfoReceived()const{ return m_protocolInfoReceived; } bool NodeInfoReceived()const{ return m_nodeInfoReceived; } bool AllQueriesCompleted()const{ return( QueryStage_Complete == m_queryStage ); } /** * Handle dead node detection tracking. * Use this routine to set state of nodes. * Tracks state as well as send notifications. */ void SetNodeAlive( bool const _isAlive ); private: void SetStaticRequests(); QueryStage m_queryStage; bool m_queryPending; bool m_queryConfiguration; uint8 m_queryRetries; bool m_protocolInfoReceived; bool m_nodeInfoReceived; bool m_manufacturerSpecificClassReceived; bool m_nodeInfoSupported; bool m_nodeAlive; //----------------------------------------------------------------------------- // Capabilities //----------------------------------------------------------------------------- public: // Security flags enum { SecurityFlag_Security = 0x01, SecurityFlag_Controller = 0x02, SecurityFlag_SpecificDevice = 0x04, SecurityFlag_RoutingSlave = 0x08, SecurityFlag_BeamCapability = 0x10, SecurityFlag_Sensor250ms = 0x20, SecurityFlag_Sensor1000ms = 0x40, SecurityFlag_OptionalFunctionality = 0x80 }; // Node Ids enum { NodeBroadcast = 0xff }; bool IsListeningDevice()const{ return m_listening; } bool IsFrequentListeningDevice()const{ return m_frequentListening; } bool IsBeamingDevice()const{ return m_beaming; } bool IsRoutingDevice()const{ return m_routing; } bool IsSecurityDevice()const{ return m_security; } uint32 GetMaxBaudRate()const{ return m_maxBaudRate; } uint8 GetVersion()const{ return m_version; } uint8 GetSecurity()const{ return m_security; } uint8 GetNodeId()const{ return m_nodeId; } uint8 GetBasic()const{ return m_basic; } uint8 GetGeneric()const{ return m_generic; } uint8 GetSpecific()const{ return m_specific; } string const& GetType()const{ return m_type; } uint32 GetNeighbors( uint8** o_associations ); bool IsController()const{ return ( m_basic == 0x01 || m_basic == 0x02 ) && ( m_generic == 0x01 || m_generic == 0x02 ); } bool IsAddingNode() const { return m_addingNode; } /* These three *AddingNode functions are used to tell if we this node is just being discovered. Currently used by the Security CC to initiate the Network Key Exchange */ void SetAddingNode() { m_addingNode = true; } void ClearAddingNode() { m_addingNode = false; } private: bool m_listening; bool m_frequentListening; bool m_beaming; bool m_routing; uint32 m_maxBaudRate; uint8 m_version; bool m_security; uint32 m_homeId; uint8 m_nodeId; uint8 m_basic; //*< Basic device class (0x01-Controller, 0x02-Static Controller, 0x03-Slave, 0x04-Routing Slave uint8 m_generic; uint8 m_specific; string m_type; // Label representing the specific/generic/basic value uint8 m_neighbors[29]; // Bitmask containing the neighbouring nodes uint8 m_numRouteNodes; // number of node routes uint8 m_routeNodes[5]; // nodes to route to map m_buttonMap; // Map button IDs into virtual node numbers bool m_addingNode; //----------------------------------------------------------------------------- // Device Naming //----------------------------------------------------------------------------- private: // Manufacturer, Product and Name are stored here so they can be set by the // user even if the device does not support the relevant command classes. string GetManufacturerName()const{ return m_manufacturerName; } string GetProductName()const{ return m_productName; } string GetNodeName()const{ return m_nodeName; } string GetLocation()const{ return m_location; } string GetManufacturerId()const{ return m_manufacturerId; } string GetProductType()const{ return m_productType; } string GetProductId()const{ return m_productId; } void SetManufacturerName( string const& _manufacturerName ){ m_manufacturerName = _manufacturerName; } void SetProductName( string const& _productName ){ m_productName = _productName; } void SetNodeName( string const& _nodeName ); void SetLocation( string const& _location ); void SetManufacturerId( string const& _manufacturerId ){ m_manufacturerId = _manufacturerId; } void SetProductType( string const& _productType ){ m_productType = _productType; } void SetProductId( string const& _productId ){ m_productId = _productId; } string m_manufacturerName; string m_productName; string m_nodeName; string m_location; string m_manufacturerId; string m_productType; string m_productId; //----------------------------------------------------------------------------- // Command Classes //----------------------------------------------------------------------------- public: /** * This function retrieves a pointer to the requested command class object (if supported by this node). * \param _commandClassId Class ID (a single byte value) identifying the command class requested. * \return Pointer to the requested CommandClass object if supported, otherwise NULL. * \see CommandClass, m_commandClassMap */ CommandClass* GetCommandClass( uint8 const _commandClassId )const; void ApplicationCommandHandler( uint8 const* _data ); /** * This function sets up Secured Command Classes. It iterates over the existing command classes marking them * as Secured if they exist, and if they don't, it creates new Command Classes and sets them up as Secured * @param _data a list of Command Classes that are Secured by the Device * @param _length the length of the _data string */ void SetSecuredClasses( uint8 const* _data, uint8 const _length ); private: /** * Creates the specified command class object and adds it to the node (via the * m_commandClassMap) if it doesn't exist. * No new object is created if it already exists for this node. * \param _commandClassId Class ID (a single byte value) identifying the command class requested. * \return Pointer to the CommandClass object just added to the map (NULL if the object * was already there or if the CommandClass object creation failed). * \see CommandClass, CommandClasses::CreateCommandClass, m_commandClassMap */ CommandClass* AddCommandClass( uint8 const _commandClassId ); /** * Removes a command class object from the node (via the m_commandClassMap). Before removing the * object, this function also removes any values stored in the object's ValueStore. * \param _commandClassId Class ID (a single byte value) identifying the command class to be removed. * \see m_commandClassMap, ValueStore, GetValueStore, ValueStore::RemoveCommandClassValues */ void RemoveCommandClass( uint8 const _commandClassId ); void ReadXML( TiXmlElement const* _nodeElement ); void ReadDeviceProtocolXML( TiXmlElement const* _ccsElement ); void ReadCommandClassesXML( TiXmlElement const* _ccsElement ); void WriteXML( TiXmlElement* _nodeElement ); map m_commandClassMap; /**< Map of command class ids and pointers to associated command class objects */ //----------------------------------------------------------------------------- // Basic commands (helpers that go through the basic command class) //----------------------------------------------------------------------------- public: void SetLevel( uint8 const _level ); //----------------------------------------------------------------------------- // On/Off commands (helpers that go through the basic or switchall command class) //----------------------------------------------------------------------------- public: void SetNodeOn(); void SetNodeOff(); //----------------------------------------------------------------------------- // Values (handled by the command classes) //----------------------------------------------------------------------------- public: ValueID CreateValueID( ValueID::ValueGenre const _genre, uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex, ValueID::ValueType const _type ); Value* GetValue( ValueID const& _id ); Value* GetValue( uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex ); bool RemoveValue( uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex ); // Helpers for creating values bool CreateValueBool( ValueID::ValueGenre const _genre, uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex, string const& _label, string const& _units, bool const _readOnly, bool const _writeOnly, bool const _default, uint8 const _pollIntensity ); bool CreateValueButton( ValueID::ValueGenre const _genre, uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex, string const& _label, uint8 const _pollIntensity ); bool CreateValueByte( ValueID::ValueGenre const _genre, uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex, string const& _label, string const& _units, bool const _readOnly, bool const _writeOnly, uint8 const _default, uint8 const _pollIntensity ); bool CreateValueDecimal( ValueID::ValueGenre const _genre, uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex, string const& _label, string const& _units, bool const _readOnly, bool const _writeOnly, string const& _default, uint8 const _pollIntensity ); bool CreateValueInt( ValueID::ValueGenre const _genre, uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex, string const& _label, string const& _units, bool const _readOnly, bool const _writeOnly, int32 const _default, uint8 const _pollIntensity ); bool CreateValueList( ValueID::ValueGenre const _genre, uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex, string const& _label, string const& _units, bool const _readOnly, bool const _writeOnly, uint8 const _size, vector const& _items, int32 const _default, uint8 const _pollIntensity ); bool CreateValueRaw( ValueID::ValueGenre const _genre, uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex, string const& _label, string const& _units, bool const _readOnly, bool const _writeOnly, uint8 const* _default, uint8 const _length, uint8 const _pollIntensity ); bool CreateValueSchedule( ValueID::ValueGenre const _genre, uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex, string const& _label, string const& _units, bool const _readOnly, bool const _writeOnly, uint8 const _pollIntensity ); bool CreateValueShort( ValueID::ValueGenre const _genre, uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex, string const& _label, string const& _units, bool const _readOnly, bool const _writeOnly, int16 const _default, uint8 const _pollIntensity ); bool CreateValueString( ValueID::ValueGenre const _genre, uint8 const _commandClassId, uint8 const _instance, uint8 const _valueIndex, string const& _label, string const& _units, bool const _readOnly, bool const _writeOnly, string const& _default, uint8 const _pollIntensity ); // helpers for removing values void RemoveValueList( ValueList* _value ); void ReadValueFromXML( uint8 const _commandClassId, TiXmlElement const* _valueElement ); bool CreateValueFromXML( uint8 const _commandClassId, TiXmlElement const* _valueElement ); private: ValueStore* GetValueStore()const{ return m_values; } ValueStore* m_values; // Values reported via command classes //----------------------------------------------------------------------------- // Configuration Parameters (handled by the Configuration command class) //----------------------------------------------------------------------------- private: bool SetConfigParam( uint8 const _param, int32 _value, uint8 const _size ); void RequestConfigParam( uint8 const _param ); bool RequestAllConfigParams( uint32 const _requestFlags ); //----------------------------------------------------------------------------- // Dynamic Values (used by query and other command classes for updating) //----------------------------------------------------------------------------- private: bool RequestDynamicValues(); //----------------------------------------------------------------------------- // Groups //----------------------------------------------------------------------------- private: // The public interface is provided via the wrappers in the Manager class uint8 GetNumGroups(); uint32 GetAssociations( uint8 const _groupIdx, uint8** o_associations ); uint8 GetMaxAssociations( uint8 const _groupIdx ); string GetGroupLabel( uint8 const _groupIdx ); void AddAssociation( uint8 const _groupIdx, uint8 const _targetNodeId ); void RemoveAssociation( uint8 const _groupIdx, uint8 const _targetNodeId ); void AutoAssociate(); // The following methods are not exposed Group* GetGroup( uint8 const _groupIdx ); // Get a pointer to a Group object. This must only be called while holding the node Lock. void AddGroup( Group* _group ); // The groups are fixed properties of a device, so there is no need for a matching RemoveGroup. void WriteGroups( TiXmlElement* _associationsElement ); // Write the group data out to XNL map m_groups; // Maps group indices to Group objects. //----------------------------------------------------------------------------- // Device Classes (static data read from the device_classes.xml file) //----------------------------------------------------------------------------- private: // Container for device class info class DeviceClass { public: DeviceClass( TiXmlElement const* _el ); ~DeviceClass(){ delete [] m_mandatoryCommandClasses; } uint8 const* GetMandatoryCommandClasses(){ return m_mandatoryCommandClasses; } uint8 GetBasicMapping(){ return m_basicMapping; } string const& GetLabel(){ return m_label; } private: uint8* m_mandatoryCommandClasses; // Zero terminated array of mandatory command classes for this device type. uint8 m_basicMapping; // Command class that COMMAND_CLASS_BASIC maps on to, or zero if there is no mapping. string m_label; // Descriptive label for the device. }; // Container for generic device class info class GenericDeviceClass : public DeviceClass { public: GenericDeviceClass( TiXmlElement const* _el ); ~GenericDeviceClass(); DeviceClass* GetSpecificDeviceClass( uint8 const& _specific ); private: map m_specificDeviceClasses; }; bool SetDeviceClasses( uint8 const _basic, uint8 const _generic, uint8 const _specific ); // Set the device class data for the node bool AddMandatoryCommandClasses( uint8 const* _commandClasses ); // Add mandatory command classes as specified in the device_classes.xml to the node. void ReadDeviceClasses(); // Read the static device class data from the device_classes.xml file string GetEndPointDeviceClassLabel( uint8 const _generic, uint8 const _specific ); static bool s_deviceClassesLoaded; // True if the xml file has alreayd been loaded static map s_basicDeviceClasses; // Map of basic device classes. static map s_genericDeviceClasses; // Map of generic device classes. //----------------------------------------------------------------------------- // Statistics //----------------------------------------------------------------------------- public: struct CommandClassData { uint8 m_commandClassId; uint32 m_sentCnt; uint32 m_receivedCnt; }; struct NodeData { uint32 m_sentCnt; uint32 m_sentFailed; uint32 m_retries; uint32 m_receivedCnt; uint32 m_receivedDups; uint32 m_receivedUnsolicited; string m_sentTS; string m_receivedTS; uint32 m_lastRequestRTT; uint32 m_averageRequestRTT; // ms uint32 m_lastResponseRTT; uint32 m_averageResponseRTT; uint8 m_quality; // Node quality measure uint8 m_lastReceivedMessage[254]; list m_ccData; }; private: void GetNodeStatistics( NodeData* _data ); uint32 m_sentCnt; // Number of messages sent from this node. uint32 m_sentFailed; // Number of sent messages failed uint32 m_retries; // Number of message retries uint32 m_receivedCnt; // Number of messages received from this node. uint32 m_receivedDups; // Number of duplicated messages received; uint32 m_receivedUnsolicited; // Number of messages received unsolicited uint32 m_lastRequestRTT; // Last message request RTT uint32 m_lastResponseRTT; // Last message response RTT TimeStamp m_sentTS; // Last message sent time TimeStamp m_receivedTS; // Last message received time uint32 m_averageRequestRTT; // Average Request round trip time. uint32 m_averageResponseRTT; // Average Reponse round trip time. uint8 m_quality; // Node quality measure uint8 m_lastReceivedMessage[254]; // Place to hold last received message uint8 m_errors; // Count errors for dead node detection }; } //namespace OpenZWave #endif //_Node_H