Add promiscuous message handling but left commented out.

Added tracking and returning driver specific message count and error data. see Manager::GetDriverStatistics
Random formatting changes for consistency.
Class SensorMultilevel will use multiple indexes for multiple sensors.
Node::GetNeighbors will only return valid data. Wait for proper query stage to complete.
On Driver shutdown stop threads then remove controller object.
Add extra comments to Driver::IsControllerCommand so we can better track commands.
Update Mac OS X example Main.cpp to be consistent with Linux version. Handle more notifications
and add a thread to wait for query completion.
Add statistics print out to both Linux and Mac OS X example apps.
Fix Mac hidapi to terminate its background CFRunLoop upon hid_close.
This commit is contained in:
glsatz 2011-08-16 20:25:14 +00:00
parent 01aa2c7853
commit 3a85a66464
12 changed files with 381 additions and 177 deletions

View file

@ -34,6 +34,7 @@
#include <pthread.h> #include <pthread.h>
#include "Options.h" #include "Options.h"
#include "Manager.h" #include "Manager.h"
#include "Driver.h"
#include "Node.h" #include "Node.h"
#include "Group.h" #include "Group.h"
#include "Notification.h" #include "Notification.h"
@ -180,6 +181,7 @@ void OnNotification
{ {
// We have received an event from the node, caused by a // We have received an event from the node, caused by a
// basic_set or hail message. // basic_set or hail message.
// TBD...
nodeInfo = nodeInfo; nodeInfo = nodeInfo;
} }
break; break;
@ -242,9 +244,9 @@ int main( int argc, char* argv[] )
pthread_mutexattr_init ( &mutexattr ); pthread_mutexattr_init ( &mutexattr );
pthread_mutexattr_settype( &mutexattr, PTHREAD_MUTEX_RECURSIVE ); pthread_mutexattr_settype( &mutexattr, PTHREAD_MUTEX_RECURSIVE );
pthread_mutex_init( &g_criticalSection, &mutexattr ); pthread_mutex_init( &g_criticalSection, &mutexattr );
pthread_mutexattr_destroy(&mutexattr); pthread_mutexattr_destroy( &mutexattr );
pthread_mutex_lock(&initMutex); pthread_mutex_lock( &initMutex );
// Create the OpenZWave Manager. // Create the OpenZWave Manager.
// The first argument is the path to the config files (where the manufacturer_specific.xml file is located // The first argument is the path to the config files (where the manufacturer_specific.xml file is located
@ -266,14 +268,15 @@ int main( int argc, char* argv[] )
string port = "/dev/ttyUSB0"; string port = "/dev/ttyUSB0";
Manager::Get()->AddDriver((argc > 1) ? argv[1] : port); Manager::Get()->AddDriver( ( argc > 1 ) ? argv[1] : port );
//Manager::Get()->AddDriver( "HID Controller", Driver::ControllerInterface_Hid ); //Manager::Get()->AddDriver( "HID Controller", Driver::ControllerInterface_Hid );
// Now we just wait for the driver to become ready, and then write out the loaded config. // Now we just wait for the driver to become ready, and then write out the loaded config.
// In a normal app, we would be handling notifications and building a UI for the user. // In a normal app, we would be handling notifications and building a UI for the user.
pthread_cond_wait(&initCond, &initMutex); pthread_cond_wait( &initCond, &initMutex );
if (!g_initFailed) { if( !g_initFailed )
{
//Manager::Get()->BeginAddNode( g_homeId ); //Manager::Get()->BeginAddNode( g_homeId );
//sleep(10); //sleep(10);
@ -283,10 +286,18 @@ int main( int argc, char* argv[] )
//Manager::Get()->EndRemoveNode( g_homeId ); //Manager::Get()->EndRemoveNode( g_homeId );
//sleep(10); //sleep(10);
Manager::Get()->WriteConfig(g_homeId); Manager::Get()->WriteConfig( g_homeId );
while(true) Driver::DriverData data;
Manager::Get()->GetDriverStatistics( g_homeId, &data );
printf("SOF: %d ACK Waiting: %d Read Aborts: %d Bad Checksums: %d\n", data.s_SOFCnt, data.s_ACKWaiting, data.s_readAborts, data.s_badChecksum);
printf("Reads: %d Writes: %d CAN: %d NAK: %d ACK: %d Out of Frame: %d\n", data.s_readCnt, data.s_writeCnt, data.s_CANCnt, data.s_NAKCnt, data.s_ACKCnt, data.s_OOFCnt);
printf("Dropped: %d Retries: %d\n", data.s_dropped, data.s_retries);
while( true )
{ {
sleep(10);
pthread_mutex_lock( &g_criticalSection ); pthread_mutex_lock( &g_criticalSection );
// Do stuff // Do stuff
pthread_mutex_unlock( &g_criticalSection ); pthread_mutex_unlock( &g_criticalSection );

View file

@ -34,6 +34,7 @@
#include <pthread.h> #include <pthread.h>
#include "Options.h" #include "Options.h"
#include "Manager.h" #include "Manager.h"
#include "Driver.h"
#include "Node.h" #include "Node.h"
#include "Group.h" #include "Group.h"
#include "Notification.h" #include "Notification.h"
@ -44,6 +45,7 @@
using namespace OpenZWave; using namespace OpenZWave;
static uint32 g_homeId = 0; static uint32 g_homeId = 0;
static bool g_initFailed = false;
typedef struct typedef struct
{ {
@ -55,6 +57,8 @@ typedef struct
static list<NodeInfo*> g_nodes; static list<NodeInfo*> g_nodes;
static pthread_mutex_t g_criticalSection; static pthread_mutex_t g_criticalSection;
static pthread_cond_t initCond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER;
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// <GetNodeInfo> // <GetNodeInfo>
@ -127,6 +131,7 @@ void OnNotification
{ {
// One of the node values has changed // One of the node values has changed
// TBD... // TBD...
nodeInfo = nodeInfo;
} }
break; break;
} }
@ -137,6 +142,7 @@ void OnNotification
{ {
// One of the node's association groups has changed // One of the node's association groups has changed
// TBD... // TBD...
nodeInfo = nodeInfo;
} }
break; break;
} }
@ -176,6 +182,7 @@ void OnNotification
// We have received an event from the node, caused by a // We have received an event from the node, caused by a
// basic_set or hail message. // basic_set or hail message.
// TBD... // TBD...
nodeInfo = nodeInfo;
} }
break; break;
} }
@ -203,11 +210,29 @@ void OnNotification
g_homeId = _notification->GetHomeId(); g_homeId = _notification->GetHomeId();
break; break;
} }
case Notification::Type_DriverFailed:
{
g_initFailed = true;
pthread_cond_broadcast(&initCond);
break;
}
case Notification::Type_AwakeNodesQueried:
case Notification::Type_AllNodesQueried:
{
pthread_cond_broadcast(&initCond);
break;
}
default:
{
}
} }
pthread_mutex_unlock( &g_criticalSection ); pthread_mutex_unlock( &g_criticalSection );
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// <main> // <main>
// Create the driver and then wait // Create the driver and then wait
@ -216,10 +241,13 @@ int main( int argc, char* argv[] )
{ {
pthread_mutexattr_t mutexattr; pthread_mutexattr_t mutexattr;
pthread_mutexattr_init ( &mutexattr );
pthread_mutexattr_settype( &mutexattr, PTHREAD_MUTEX_RECURSIVE ); pthread_mutexattr_settype( &mutexattr, PTHREAD_MUTEX_RECURSIVE );
pthread_mutex_init( &g_criticalSection, &mutexattr ); pthread_mutex_init( &g_criticalSection, &mutexattr );
pthread_mutexattr_destroy( &mutexattr ); pthread_mutexattr_destroy( &mutexattr );
pthread_mutex_lock( &initMutex );
// Create the OpenZWave Manager. // Create the OpenZWave Manager.
// The first argument is the path to the config files (where the manufacturer_specific.xml file is located // The first argument is the path to the config files (where the manufacturer_specific.xml file is located
// The second argument is the path for saved Z-Wave network state and the log file. If you leave it NULL // The second argument is the path for saved Z-Wave network state and the log file. If you leave it NULL
@ -237,15 +265,18 @@ int main( int argc, char* argv[] )
// Add a Z-Wave Driver // Add a Z-Wave Driver
// Modify this line to set the correct serial port for your PC interface. // Modify this line to set the correct serial port for your PC interface.
Manager::Get()->AddDriver( "/dev/cu.usbserial");
string port = "/dev/cu.usbserial";
Manager::Get()->AddDriver( ( argc > 1 ) ? argv[1] : port );
//Manager::Get()->AddDriver( "HID Controller", Driver::ControllerInterface_Hid ); //Manager::Get()->AddDriver( "HID Controller", Driver::ControllerInterface_Hid );
// Now we just wait for the driver to become ready, and then write out the loaded config. // Now we just wait for the driver to become ready, and then write out the loaded config.
// In a normal app, we would be handling notifications and building a UI for the user. // In a normal app, we would be handling notifications and building a UI for the user.
while( !g_homeId ) pthread_cond_wait( &initCond, &initMutex );
if( !g_initFailed )
{ {
sleep(1);
}
//Manager::Get()->BeginAddNode( g_homeId ); //Manager::Get()->BeginAddNode( g_homeId );
//sleep(10); //sleep(10);
@ -253,9 +284,16 @@ int main( int argc, char* argv[] )
//Manager::Get()->BeginRemoveNode( g_homeId ); //Manager::Get()->BeginRemoveNode( g_homeId );
//sleep(10); //sleep(10);
//Manager::Get()->EndRemoveNode( g_homeId ); //Manager::Get()->EndRemoveNode( g_homeId );
sleep(10); //sleep(10);
Manager::Get()->WriteConfig( g_homeId ); Manager::Get()->WriteConfig( g_homeId );
Driver::DriverData data;
Manager::Get()->GetDriverStatistics( g_homeId, &data );
printf("SOF: %d ACK Waiting: %d Read Aborts: %d Bad Checksums: %d\n", data.s_SOFCnt, data.s_ACKWaiting, data.s_readAborts, data.s_badChecksum);
printf("Reads: %d Writes: %d CAN: %d NAK: %d ACK: %d Out of Frame: %d\n", data.s_readCnt, data.s_writeCnt, data.s_CANCnt, data.s_NAKCnt, data.s_ACKCnt, data.s_OOFCnt);
printf("Dropped: %d Retries: %d\n", data.s_dropped, data.s_retries);
while( true ) while( true )
{ {
sleep(10); sleep(10);
@ -263,7 +301,11 @@ int main( int argc, char* argv[] )
pthread_mutex_lock( &g_criticalSection ); pthread_mutex_lock( &g_criticalSection );
// Do stuff // Do stuff
pthread_mutex_unlock( &g_criticalSection ); pthread_mutex_unlock( &g_criticalSection );
sleep(5);
} }
}
Manager::Destroy();
pthread_mutex_destroy( &g_criticalSection ); pthread_mutex_destroy( &g_criticalSection );
return 0; return 0;

View file

@ -42,6 +42,7 @@ struct hid_device_ {
int uses_numbered_reports; int uses_numbered_reports;
int disconnected; int disconnected;
CFStringRef run_loop_mode; CFStringRef run_loop_mode;
CFRunLoopRef runloopref;
uint8_t *input_report_buf; uint8_t *input_report_buf;
struct input_report *input_reports; struct input_report *input_reports;
pthread_mutex_t mutex; pthread_mutex_t mutex;
@ -624,7 +625,8 @@ int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
Need to get some from the OS. */ Need to get some from the OS. */
/* Move the device's run loop to this thread. */ /* Move the device's run loop to this thread. */
IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode); dev->runloopref = CFRunLoopGetCurrent();
IOHIDDeviceScheduleWithRunLoop(dev->device_handle, dev->runloopref, dev->run_loop_mode);
if (dev->blocking) { if (dev->blocking) {
/* Run the Run Loop until it stops timing out. In other /* Run the Run Loop until it stops timing out. In other
@ -729,6 +731,8 @@ void HID_API_EXPORT hid_close(hid_device *dev)
if (!dev) if (!dev)
return; return;
CFRunLoopStop(dev->runloopref);
/* Close the OS handle to the device, but only if it's not /* Close the OS handle to the device, but only if it's not
been unplugged. If it's been unplugged, then calling been unplugged. If it's been unplugged, then calling
IOHIDDeviceClose() will crash. */ IOHIDDeviceClose() will crash. */

View file

@ -144,6 +144,8 @@ namespace OpenZWave
#define FUNC_ID_ZW_IS_FAILED_NODE_ID 0x62 // Check to see if a specified node has failed #define FUNC_ID_ZW_IS_FAILED_NODE_ID 0x62 // Check to see if a specified node has failed
#define FUNC_ID_ZW_REPLACE_FAILED_NODE 0x63 // Remove a failed node from the controller's list (?) #define FUNC_ID_ZW_REPLACE_FAILED_NODE 0x63 // Remove a failed node from the controller's list (?)
#define FUNC_ID_ZW_GET_ROUTING_INFO 0x80 // Get a specified node's neighbor information from the controller #define FUNC_ID_ZW_GET_ROUTING_INFO 0x80 // Get a specified node's neighbor information from the controller
#define FUNC_ID_ZW_SET_PROMISCUOUS_MODE 0xD0 // Set controller into promiscuous mode to listen to all frames
#define FUNC_ID_PROMISCUOUS_APPLICATION_COMMAND_HANDLER 0xD1
#define ADD_NODE_ANY 0x01 #define ADD_NODE_ANY 0x01
#define ADD_NODE_CONTROLLER 0x02 #define ADD_NODE_CONTROLLER 0x02

View file

@ -116,7 +116,17 @@ Driver::Driver
m_controllerCallback( NULL ), m_controllerCallback( NULL ),
m_controllerCallbackContext( NULL ), m_controllerCallbackContext( NULL ),
m_controllerAdded( false ), m_controllerAdded( false ),
m_controllerCommandNode( 0 ) m_controllerCommandNode( 0 ),
m_SOFCnt( 0 ),
m_ACKWaiting( 0 ),
m_readAborts( 0 ),
m_badChecksum( 0 ),
m_readCnt( 0 ),
m_writeCnt( 0 ),
m_CANCnt( 0 ),
m_NAKCnt( 0 ),
m_ACKCnt( 0 ),
m_OOFCnt( 0 )
{ {
// Clear the nodes array // Clear the nodes array
memset( m_nodes, 0, sizeof(Node*) * 256 ); memset( m_nodes, 0, sizeof(Node*) * 256 );
@ -159,11 +169,12 @@ Driver::~Driver
m_exitEvent->Set(); m_exitEvent->Set();
m_wakeEvent->Set(); m_wakeEvent->Set();
m_pollThread->Stop(); m_pollThread->Stop();
m_controllerThread->Stop();
m_driverThread->Stop();
delete m_controller; delete m_controller;
m_controllerThread->Stop();
m_driverThread->Stop();
delete m_pollThread; delete m_pollThread;
delete m_controllerThread; delete m_controllerThread;
delete m_driverThread; delete m_driverThread;
@ -301,6 +312,7 @@ void Driver::DriverThreadProc
{ {
node->m_queryStageCompleted = true; node->m_queryStageCompleted = true;
} }
m_dropped++;
RemoveMsg(); RemoveMsg();
} }
else else
@ -329,6 +341,7 @@ void Driver::DriverThreadProc
{ {
node->m_queryStageCompleted = true; node->m_queryStageCompleted = true;
} }
m_dropped++;
RemoveMsg(); RemoveMsg();
} }
} }
@ -336,7 +349,8 @@ void Driver::DriverThreadProc
{ {
// Node is listening, so we'll just retry sending the message. // Node is listening, so we'll just retry sending the message.
Log::Write( "Resending message (attempt %d)", msg->GetSendAttempts() ); Log::Write( "Resending message (attempt %d)", msg->GetSendAttempts() );
} m_retries++;
}
} }
} }
} }
@ -424,6 +438,10 @@ bool Driver::Init
{ {
SendMsg(*it); SendMsg(*it);
} }
//If we ever want promiscuous mode uncomment this code.
//Msg* msg = new Msg( "FUNC_ID_ZW_SET_PROMISCUOUS_MODE", 0xff, REQUEST, FUNC_ID_ZW_SET_PROMISCUOUS_MODE, false, false );
//msg->Append( 0xff );
//SendMsg( msg );
// Init successful // Init successful
return true; return true;
@ -769,6 +787,7 @@ bool Driver::WriteMsg()
Log::Write( "" ); Log::Write( "" );
Log::Write( "Sending command (Callback ID=0x%.2x, Expected Reply=0x%.2x) - %s", msg->GetCallbackId(), msg->GetExpectedReply(), msg->GetAsString().c_str() ); Log::Write( "Sending command (Callback ID=0x%.2x, Expected Reply=0x%.2x) - %s", msg->GetCallbackId(), msg->GetExpectedReply(), msg->GetAsString().c_str() );
m_controller->Write( msg->GetBuffer(), msg->GetLength() ); m_controller->Write( msg->GetBuffer(), msg->GetLength() );
m_writeCnt++;
dataWritten = true; dataWritten = true;
} }
else else
@ -945,14 +964,16 @@ bool Driver::IsControllerCommand
// ranges of commands are used to enhance performance // ranges of commands are used to enhance performance
// the commands identified as "Controller Commands" needs to be reviewed as we // the commands identified as "Controller Commands" needs to be reviewed as we
// understand the protocol better and implement handlers // understand the protocol better and implement handlers
if( ( _command >= FUNC_ID_ZW_SET_DEFAULT ) && if( ( _command >= FUNC_ID_ZW_SET_DEFAULT ) && // 0x42
( _command <= FUNC_ID_ZW_REQUEST_NODE_NEIGHBOR_UPDATE ) ) ( _command <= FUNC_ID_ZW_REQUEST_NODE_NEIGHBOR_UPDATE ) ) // 0x48
return true; return true;
if( ( _command >= FUNC_ID_ZW_ADD_NODE_TO_NETWORK ) && if( ( _command >= FUNC_ID_ZW_ADD_NODE_TO_NETWORK ) && // 0x4a
( _command <= FUNC_ID_ZW_SET_LEARN_MODE ) ) ( _command <= FUNC_ID_ZW_SET_LEARN_MODE ) ) // 0x50
return true; return true;
if( ( _command >= FUNC_ID_ZW_REMOVE_FAILED_NODE_ID ) && if( ( _command >= FUNC_ID_ZW_REMOVE_FAILED_NODE_ID ) && // 0x61
( _command <= FUNC_ID_ZW_REPLACE_FAILED_NODE ) ) ( _command <= FUNC_ID_ZW_REPLACE_FAILED_NODE ) ) // 0x63
return true;
if( _command == FUNC_ID_ZW_GET_ROUTING_INFO ) // 0x80
return true; return true;
return false; return false;
@ -982,9 +1003,11 @@ bool Driver::ReadMsg
{ {
case SOF: case SOF:
{ {
m_SOFCnt++;
if( m_waitingForAck ) if( m_waitingForAck )
{ {
Log::Write( "Unsolicited message received while waiting for ACK." ); Log::Write( "Unsolicited message received while waiting for ACK." );
m_ACKWaiting++;
} }
// Read the length byte. Keep trying until we get it. // Read the length byte. Keep trying until we get it.
@ -1005,6 +1028,7 @@ bool Driver::ReadMsg
if( loops == 10 ) if( loops == 10 )
{ {
Log::Write( "100ms passed without finding the length byte...aborting frame read"); Log::Write( "100ms passed without finding the length byte...aborting frame read");
m_readAborts++;
break; break;
} }
@ -1028,6 +1052,7 @@ bool Driver::ReadMsg
if( loops == 50 ) if( loops == 50 )
{ {
Log::Write( "500ms passed without reading the rest of the frame...aborting frame read" ); Log::Write( "500ms passed without reading the rest of the frame...aborting frame read" );
m_readAborts++;
break; break;
} }
@ -1061,12 +1086,14 @@ bool Driver::ReadMsg
uint8 ack = ACK; uint8 ack = ACK;
m_controller->Write( &ack, 1 ); m_controller->Write( &ack, 1 );
m_readCnt++;
// Process the received message // Process the received message
ProcessMsg( &buffer[2] ); ProcessMsg( &buffer[2] );
} }
else else
{ {
Log::Write( "Checksum incorrect - sending NAK" ); Log::Write( "Checksum incorrect - sending NAK" );
m_badChecksum++;
uint8 nak = NAK; uint8 nak = NAK;
m_controller->Write( &nak, 1 ); m_controller->Write( &nak, 1 );
} }
@ -1076,6 +1103,7 @@ bool Driver::ReadMsg
case CAN: case CAN:
{ {
Log::Write( "CAN received...triggering resend" ); Log::Write( "CAN received...triggering resend" );
m_CANCnt++;
TriggerResend(); TriggerResend();
break; break;
} }
@ -1083,6 +1111,7 @@ bool Driver::ReadMsg
case NAK: case NAK:
{ {
Log::Write( "NAK received...triggering resend" ); Log::Write( "NAK received...triggering resend" );
m_NAKCnt++;
TriggerResend(); TriggerResend();
break; break;
} }
@ -1090,6 +1119,7 @@ bool Driver::ReadMsg
case ACK: case ACK:
{ {
Log::Write( " ACK received CallbackId 0x%.2x Reply 0x%.2x", m_expectedCallbackId, m_expectedReply ); Log::Write( " ACK received CallbackId 0x%.2x Reply 0x%.2x", m_expectedCallbackId, m_expectedReply );
m_ACKCnt++;
m_waitingForAck = false; m_waitingForAck = false;
if( ( 0 == m_expectedCallbackId ) && ( 0 == m_expectedReply ) ) if( ( 0 == m_expectedCallbackId ) && ( 0 == m_expectedReply ) )
@ -1103,6 +1133,7 @@ bool Driver::ReadMsg
default: default:
{ {
Log::Write( "ERROR! Out of frame flow! (0x%.2x). Sending NAK.", buffer[0] ); Log::Write( "ERROR! Out of frame flow! (0x%.2x). Sending NAK.", buffer[0] );
m_OOFCnt++;
uint8 nak = NAK; uint8 nak = NAK;
m_controller->Write( &nak, 1 ); m_controller->Write( &nak, 1 );
break; break;
@ -1399,6 +1430,12 @@ void Driver::ProcessMsg
HandleReplaceFailedNodeRequest( _data ); HandleReplaceFailedNodeRequest( _data );
break; break;
} }
case FUNC_ID_PROMISCUOUS_APPLICATION_COMMAND_HANDLER:
{
Log::Write( "" );
HandlePromiscuousApplicationCommandHandlerRequest( _data );
break;
}
default: default:
{ {
break; break;
@ -1990,6 +2027,7 @@ bool Driver::HandleSendDataRequest
{ {
node->m_queryStageCompleted = true; node->m_queryStageCompleted = true;
} }
m_dropped++;
RemoveMsg(); RemoveMsg();
messageRemoved = true; messageRemoved = true;
} }
@ -2461,6 +2499,21 @@ void Driver::HandleApplicationCommandHandlerRequest
} }
} }
//-----------------------------------------------------------------------------
// <Driver::HandlePromiscuousApplicationCommandHandlerRequest>
// Process a request from the Z-Wave PC interface when in promiscuous mode.
//-----------------------------------------------------------------------------
void Driver::HandlePromiscuousApplicationCommandHandlerRequest
(
uint8* _data
)
{
//uint8 nodeId = _data[3];
//uint8 len = _data[4];
//uint8 classId = _data[5];
//uint8 destNodeId = _data[5+len];
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// <Driver::HandleAssignReturnRouteRequest> // <Driver::HandleAssignReturnRouteRequest>
// Process a request from the Z-Wave PC interface // Process a request from the Z-Wave PC interface
@ -4339,3 +4392,27 @@ bool Driver::HandleReadMemoryResponse
Log::Write("Received reply to FUNC_ID_MEMORY_GET_BYTE"); Log::Write("Received reply to FUNC_ID_MEMORY_GET_BYTE");
return res; return res;
} }
//-----------------------------------------------------------------------------
// <Driver::GetDriverStatistics>
// Return driver statistics
//-----------------------------------------------------------------------------
void Driver::GetDriverStatistics
(
DriverData* _data
)
{
_data->s_SOFCnt = m_SOFCnt;
_data->s_ACKWaiting = m_ACKWaiting;
_data->s_readAborts = m_readAborts;
_data->s_badChecksum = m_badChecksum;
_data->s_readCnt = m_readCnt;
_data->s_writeCnt = m_writeCnt;
_data->s_writeCnt = m_writeCnt;
_data->s_CANCnt = m_CANCnt;
_data->s_NAKCnt = m_NAKCnt;
_data->s_ACKCnt = m_ACKCnt;
_data->s_OOFCnt = m_OOFCnt;
_data->s_dropped = m_dropped;
_data->s_retries = m_retries;
}

View file

@ -319,6 +319,7 @@ namespace OpenZWave
void HandleReplaceFailedNodeRequest( uint8* _data ); void HandleReplaceFailedNodeRequest( uint8* _data );
void HandleRemoveNodeFromNetworkRequest( uint8* _data ); void HandleRemoveNodeFromNetworkRequest( uint8* _data );
void HandleApplicationCommandHandlerRequest( uint8* _data ); void HandleApplicationCommandHandlerRequest( uint8* _data );
void HandlePromiscuousApplicationCommandHandlerRequest( uint8* _data );
void HandleAssignReturnRouteRequest( uint8* _data ); void HandleAssignReturnRouteRequest( uint8* _data );
void HandleDeleteReturnRouteRequest( uint8* _data ); void HandleDeleteReturnRouteRequest( uint8* _data );
void HandleNodeNeighborUpdateRequest( uint8* _data ); void HandleNodeNeighborUpdateRequest( uint8* _data );
@ -529,9 +530,44 @@ namespace OpenZWave
void NotifyWatchers(); // Passes the notifications to all the registered watcher callbacks in turn. void NotifyWatchers(); // Passes the notifications to all the registered watcher callbacks in turn.
list<Notification*> m_notifications; list<Notification*> m_notifications;
//-----------------------------------------------------------------------------
// Statistics
//-----------------------------------------------------------------------------
public:
struct DriverData
{
uint32 s_SOFCnt; // Number of SOF bytes received
uint32 s_ACKWaiting; // Number of unsolcited messages while waiting for an ACK
uint32 s_readAborts; // Number of times read were aborted due to timeouts
uint32 s_badChecksum; // Number of bad checksums
uint32 s_readCnt; // Number of messages successfully read
uint32 s_writeCnt; // Number of messages successfully sent
uint32 s_CANCnt; // Number of CAN bytes received
uint32 s_NAKCnt; // Number of NAK bytes received
uint32 s_ACKCnt; // Number of ACK bytes received
uint32 s_OOFCnt; // Number of bytes out of framing
uint32 s_dropped; // Number of messages dropped & not delivered
uint32 s_retries; // Number of messages retransmitted
};
private:
void GetDriverStatistics( DriverData* _data );
uint32 m_SOFCnt; // Number of SOF bytes received
uint32 m_ACKWaiting; // Number of unsolcited messages while waiting for an ACK
uint32 m_readAborts; // Number of times read were aborted due to timeouts
uint32 m_badChecksum; // Number of bad checksums
uint32 m_readCnt; // Number of messages successfully read
uint32 m_writeCnt; // Number of messages successfully sent
uint32 m_CANCnt; // Number of CAN bytes received
uint32 m_NAKCnt; // Number of NAK bytes received
uint32 m_ACKCnt; // Number of ACK bytes received
uint32 m_OOFCnt; // Number of bytes out of framing
uint32 m_dropped; // Number of messages dropped & not delivered
uint32 m_retries; // Number of messages retransmitted
}; };
} // namespace OpenZWave } // namespace OpenZWave
#endif // _Driver_H #endif // _Driver_H

View file

@ -3329,3 +3329,20 @@ bool Manager::ActivateScene
return false; return false;
} }
//-----------------------------------------------------------------------------
// <Manager::DriverStatistics>
// Retrieve driver based counters.
//-----------------------------------------------------------------------------
void Manager::GetDriverStatistics
(
uint32 const _homeId,
Driver::DriverData* _data
)
{
if( Driver* driver = GetDriver( _homeId ) )
{
driver->GetDriverStatistics( _data );
}
}

View file

@ -1660,8 +1660,24 @@ namespace OpenZWave
bool ActivateScene( uint8 const _sceneId ); bool ActivateScene( uint8 const _sceneId );
/*@}*/ /*@}*/
};
//-----------------------------------------------------------------------------
// Statistics interface
//-----------------------------------------------------------------------------
/** \name Statistics retrieval interface
* Commands for Z-Wave statistics interface.
*/
/*@{*/
public:
/**
* \brief Retrieve statistics from driver
* \param _homeId The Home ID of the driver to obtain counters
* \param _data Pointer to structure DriverData to return values
*/
void GetDriverStatistics( uint32 const _homeId, Driver::DriverData* _data );
};
/*@}*/
} // namespace OpenZWave } // namespace OpenZWave
#endif // _Manager_H #endif // _Manager_H

View file

@ -517,6 +517,11 @@ uint32 Node::GetNeighbors
// determine how many neighbors there are // determine how many neighbors there are
int i; int i;
uint32 numNeighbors = 0; uint32 numNeighbors = 0;
if( m_queryStage < QueryStage_Session )
{
*o_neighbors = NULL;
return 0;
}
for( i = 0; i < 29; i++ ) for( i = 0; i < 29; i++ )
{ {
for( unsigned char mask = 0x80; mask != 0; mask >>= 1 ) for( unsigned char mask = 0x80; mask != 0; mask >>= 1 )

View file

@ -166,48 +166,53 @@ bool SensorMultilevel::HandleMsg
if (SensorMultilevelCmd_Report == (SensorMultilevelCmd)_data[0]) if (SensorMultilevelCmd_Report == (SensorMultilevelCmd)_data[0])
{ {
uint8 scale; uint8 scale;
uint8 sensorType = _data[1];
string valueStr = ExtractValue( &_data[2], &scale ); string valueStr = ExtractValue( &_data[2], &scale );
if( ValueDecimal* value = static_cast<ValueDecimal*>( GetValue( _instance, 0 ) ) ) Node* node = GetNodeUnsafe();
if( node != NULL )
{ {
value->SetLabel( c_sensorTypeNames[_data[1]] ); ValueDecimal* value = static_cast<ValueDecimal*>( GetValue( _instance, sensorType ) );
switch( _data[1] ) if( value == NULL)
{ {
case SensorType_Temperature: value->SetUnits( scale ? "F" : "C" ); break; char const* units = "";
case SensorType_General: value->SetUnits( scale ? "" : "%" ); break; switch( sensorType )
case SensorType_Luminance: value->SetUnits( scale ? "lux" : "%" ); break; {
case SensorType_Power: value->SetUnits( scale ? "BTU/h" : "W" ); break; case SensorType_Temperature: units = scale ? "F" : "C"; break;
case SensorType_RelativeHumidity: value->SetUnits( "%" ); break; case SensorType_General: units = scale ? "" : "%"; break;
case SensorType_Velocity: value->SetUnits( scale ? "mph" : "m/s" ); break; case SensorType_Luminance: units = scale ? "lux" : "%"; break;
case SensorType_Direction: value->SetUnits( "" ); break; case SensorType_Power: units = scale ? "BTU/h" : "W"; break;
case SensorType_AtmosphericPressure: value->SetUnits( scale ? "inHg" : "kPa" ); break; case SensorType_RelativeHumidity: units = "%"; break;
case SensorType_BarometricPressure: value->SetUnits( scale ? "inHg" : "kPa" ); break; case SensorType_Velocity: units = scale ? "mph" : "m/s"; break;
case SensorType_SolarRadiation: value->SetUnits( "W/m2" ); break; case SensorType_Direction: units = ""; break;
case SensorType_DewPoint: value->SetUnits( scale ? "in/h" : "mm/h" ); break; case SensorType_AtmosphericPressure: units = scale ? "inHg" : "kPa"; break;
case SensorType_RainRate: value->SetUnits( scale ? "F" : "C" ); break; case SensorType_BarometricPressure: units = scale ? "inHg" : "kPa"; break;
case SensorType_TideLevel: value->SetUnits( scale ? "ft" : "m" ); break; case SensorType_SolarRadiation: units = "W/m2"; break;
case SensorType_Weight: value->SetUnits( scale ? "lb" : "kg" ); break; case SensorType_DewPoint: units = scale ? "in/h" : "mm/h"; break;
case SensorType_Voltage: value->SetUnits( scale ? "mV" : "V" ); break; case SensorType_RainRate: units = scale ? "F" : "C"; break;
case SensorType_Current: value->SetUnits( scale ? "mA" : "A" ); break; case SensorType_TideLevel: units = scale ? "ft" : "m"; break;
case SensorType_CO2: value->SetUnits( "ppm" ); break; case SensorType_Weight: units = scale ? "lb" : "kg"; break;
case SensorType_AirFlow: value->SetUnits( scale ? "cfm" : "m3/h" ); break; case SensorType_Voltage: units = scale ? "mV" : "V"; break;
case SensorType_TankCapacity: value->SetUnits( c_tankCapcityUnits[scale] ); break; case SensorType_Current: units = scale ? "mA" : "A"; break;
case SensorType_Distance: value->SetUnits( c_distanceUnits[scale] ); break; case SensorType_CO2: units = "ppm"; break;
case SensorType_AirFlow: units = scale ? "cfm" : "m3/h"; break;
case SensorType_TankCapacity: units = c_tankCapcityUnits[scale]; break;
case SensorType_Distance: units = c_distanceUnits[scale]; break;
default: break; default: break;
} }
value = node->CreateValueDecimal( ValueID::ValueGenre_User, GetCommandClassId(), _instance, sensorType, c_sensorTypeNames[sensorType], units, true, false, "0.0" );
}
Log::Write( "Received SensorMultiLevel report from node %d, instance %d: value=%s%s", GetNodeId(), _instance, valueStr.c_str(), value->GetUnits().c_str() ); Log::Write( "Received SensorMultiLevel report from node %d, instance %d: value=%s%s", GetNodeId(), _instance, valueStr.c_str(), value->GetUnits().c_str() );
value->OnValueChanged( valueStr ); value->OnValueChanged( valueStr );
}
Node* node = GetNodeUnsafe(); if( node->m_queryPending )
if( node != NULL && node->m_queryPending )
{ {
node->m_queryStageCompleted = true; node->m_queryStageCompleted = true;
} }
return true; return true;
} }
}
return false; return false;
} }
@ -220,10 +225,7 @@ void SensorMultilevel::CreateVars
uint8 const _instance uint8 const _instance
) )
{ {
if( Node* node = GetNodeUnsafe() ) // Don't create anything here. We do it in the report.
{
node->CreateValueDecimal( ValueID::ValueGenre_User, GetCommandClassId(), _instance, 0, "Unknown", "", true, false, "0.0" );
}
} }

View file

@ -89,18 +89,18 @@ bool HidControllerImpl::Open
m_hHidController = hid_open( _vendorId, _productId, NULL ); m_hHidController = hid_open( _vendorId, _productId, NULL );
if ( !m_hHidController ) if ( !m_hHidController )
{ {
Log::Write( "Cannot find specified HID port with VID:%04hx and PID:0x%04hx.\n", _vendorId, _productId ); Log::Write( "Cannot find specified HID port with VID:%04hx and PID:0x%04hx.", _vendorId, _productId );
// Enumerate connected HIDs for debugging purposes // Enumerate connected HIDs for debugging purposes
// Note: most OS intentionally hide keyboard/mouse devices from HID access // Note: most OS intentionally hide keyboard/mouse devices from HID access
struct hid_device_info *devices, *currentDevice; struct hid_device_info *devices, *currentDevice;
devices = hid_enumerate(0x0, 0x0); devices = hid_enumerate( 0x0, 0x0 );
currentDevice = devices; currentDevice = devices;
Log::Write( "Enumerating connected HIDs:\n" ); Log::Write( "Enumerating connected HIDs:" );
while (currentDevice) while( currentDevice )
{ {
Log::Write( "\tVID:%04hx\tPID:0x%04hx\tSN:%ls\tMfg:%ls\tProd:%ls\tPath:%s\n", Log::Write( "\tVID:%04hx\tPID:0x%04hx\tSN:%ls\tMfg:%ls\tProd:%ls\tPath:%s",
currentDevice->vendor_id, currentDevice->vendor_id,
currentDevice->product_id, currentDevice->product_id,
currentDevice->serial_number, currentDevice->serial_number,
@ -222,7 +222,7 @@ uint32 HidControllerImpl::Read
if( !m_hidControllerOpen ) if( !m_hidControllerOpen )
{ {
//Error //Error
Log::Write( "Error: HID port must be opened before reading\n" ); Log::Write( "Error: HID port must be opened before reading" );
return 0; return 0;
} }
@ -275,8 +275,7 @@ uint32 HidControllerImpl::Read
return _length; return _length;
HidPortError: HidPortError:
//Error //Error
Log::Write( "Error: HID port returned error reading rest of packet: 0x%08hx, HIDAPI error string:", m_hidFeatureReportReadBufferBytes ); Log::Write( "Error: HID port returned error reading rest of packet: %d, HIDAPI error string: %ls", m_hidFeatureReportReadBufferBytes, hid_error(m_hHidController));
Log::Write("%ls\n", hid_error(m_hHidController));
return 0; return 0;
} }
@ -293,14 +292,14 @@ uint32 HidControllerImpl::Write
if( !m_hidControllerOpen ) if( !m_hidControllerOpen )
{ {
//Error //Error
Log::Write( "Error: HID port must be opened before writing\n" ); Log::Write( "Error: HID port must be opened before writing" );
return 0; return 0;
} }
if ( FEATURE_REPORT_LENGTH - 2 < _length) if ( FEATURE_REPORT_LENGTH - 2 < _length)
{ {
//Error //Error
Log::Write( "Error: Write buffer length %d exceeded feature report data capacity %d\n", Log::Write( "Error: Write buffer length %d exceeded feature report data capacity %d",
_length, _length,
FEATURE_REPORT_LENGTH - 2 ); FEATURE_REPORT_LENGTH - 2 );
return 0; return 0;
@ -320,9 +319,7 @@ uint32 HidControllerImpl::Write
if (bytesSent < 2) if (bytesSent < 2)
{ {
//Error //Error
Log::Write( "Error: HID port returned error sending bytes: 0x%08hx, HIDAPI error string:", bytesSent ); Log::Write( "Error: HID port returned error sending bytes: %d, HIDAPI error string: %ls", bytesSent, hid_error( m_hHidController ) );
const wchar_t* errString = hid_error( m_hHidController );
Log::Write( "%ls\n", errString );
return 0; return 0;
} }
@ -373,18 +370,13 @@ bool HidControllerImpl::Wait
default: default:
{ {
//Error //Error
Log::Write( "Error: HID port returned unexpected input report data in byte 2 during Wait(): 0x%08hx\n", inputReport[2] ); Log::Write( "Error: HID port returned unexpected input report data in byte 2 during Wait(): 0x%08hx", inputReport[2] );
return false; return false;
break; break;
} }
} }
// continue looping until some rx feature report data is reported via input report // continue looping until some rx feature report data is reported via input report
} }
//Error
Log::Write( "Error: HID port returned error reading input bytes: 0x%08hx, HIDAPI error string:", hidApiResult );
const wchar_t* errString = hid_error( m_hHidController );
Log::Write( "%ls\n", errString );
} }
return false; return false;
@ -406,7 +398,7 @@ int HidControllerImpl::GetFeatureReport
result = hid_get_feature_report( m_hHidController, _buffer, _length ); result = hid_get_feature_report( m_hHidController, _buffer, _length );
if (result < 0) if (result < 0)
{ {
Log::Write( "Error: HID GetFeatureReport on ID 0x%hx returned (0x%.8x)\n", _reportId, result ); Log::Write( "Error: HID GetFeatureReport on ID 0x%hx returned (0x%.8x)", _reportId, result );
} }
return result; return result;
} }
@ -426,7 +418,7 @@ int HidControllerImpl::SendFeatureReport
result = hid_send_feature_report( m_hHidController, _data, _length ); result = hid_send_feature_report( m_hHidController, _data, _length );
if (result < 0) if (result < 0)
{ {
Log::Write( "Error: HID SendFeatureReport on ID 0x%hx returned (0x%.8x)\n", _data[0], result ); Log::Write( "Error: HID SendFeatureReport on ID 0x%hx returned (0x%.8x)", _data[0], result );
} }
return result; return result;
} }