Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial repo set-up

  • Loading branch information...
commit 86032865246ef02cb9936b985b9779be66f460cf 1 parent 9a9fbdc
Elias Karakoulakis authored
View
332 Main.cpp
@@ -0,0 +1,332 @@
+//
+// OpenZWave hub for Project Ansible
+// (c) 2011 Elias Karakoulakis <elias.karakoulakis@gmail.com>
+//
+
+#include "RemoteManager.h"
+#include <protocol/TBinaryProtocol.h>
+#include <server/TSimpleServer.h>
+#include <transport/TServerSocket.h>
+#include <transport/TBufferTransports.h>
+
+using namespace ::apache::thrift;
+using namespace ::apache::thrift::protocol;
+using namespace ::apache::thrift::transport;
+using namespace ::apache::thrift::server;
+
+using boost::shared_ptr;
+
+using namespace OpenZWave;
+
+// boost: extra includes
+#include <boost/thread.hpp>
+// the global mutex for accessing OpenZWave library
+static boost::recursive_mutex g_criticalSection;
+
+//
+// Struct to hold all valid OpenZWave ValueID's
+// (used by RemoteManager_server.cpp)
+typedef struct
+{
+ uint32 m_homeId;
+ uint8 m_nodeId;
+ bool m_polled;
+ std::map<uint64, ValueID*> m_values;
+} NodeInfo;
+//
+static list<NodeInfo*> g_nodes;
+static std::map<uint64, ValueID*> g_values;
+// OpenZWave includes
+#include "Notification.h"
+
+// PocoStromp
+#include "PocoStomp.h"
+
+
+
+static uint32 g_homeId = 0;
+static bool g_initFailed = false;
+//
+
+static boost::condition_variable initCond ;
+static boost::mutex initMutex;
+
+// STOMP statics
+static STOMP::PocoStomp* stomp_client;
+static string* notifications_topic = new string("/queue/zwave/monitor");
+
+//-----------------------------------------------------------------------------
+// <GetNodeInfo>
+// Callback that is triggered when a value, group or node changes
+//-----------------------------------------------------------------------------
+NodeInfo* GetNodeInfo
+(
+ Notification const* _notification
+)
+{
+ uint32 const homeId = _notification->GetHomeId();
+ uint8 const nodeId = _notification->GetNodeId();
+ for( list<NodeInfo*>::iterator it = g_nodes.begin(); it != g_nodes.end(); ++it )
+ {
+ NodeInfo* nodeInfo = *it;
+ if( ( nodeInfo->m_homeId == homeId ) && ( nodeInfo->m_nodeId == nodeId ) )
+ {
+ return nodeInfo;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// <OnNotification>
+// Callback that is triggered by OpenZWave when a value, group or node changes
+//-----------------------------------------------------------------------------
+void OnNotification
+(
+ Notification const* _notification,
+ void* _context
+)
+{
+ // Must do this inside a critical section to avoid conflicts with the main thread
+ g_criticalSection.lock();
+
+ switch( _notification->GetType() )
+ {
+ /**< A new node value has been added to OpenZWave's list.
+ These notifications occur after a node has been discovered,
+ and details of its command classes have been received.
+ Each command class may generate one or more values,
+ depending on the complexity of the item being represented. */
+ case Notification::Type_ValueAdded:
+ {
+ if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
+ {
+ // Add the new value to the node's value list
+ ValueID v = _notification->GetValueID();
+ uint64 key = v.GetId();
+ nodeInfo->m_values[ key] = &v;
+ // ekarak: also add it to global ValueID map
+ g_values[ key ] = &v;
+ }
+ break;
+ }
+
+ /**< A node value has been removed from OpenZWave's list.
+ This only occurs when a node is removed. */
+ case Notification::Type_ValueRemoved:
+ {
+ if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
+ {
+ // Remove the value from out list
+ // ekarak: no need to iterate a map
+ nodeInfo->m_values.erase(_notification->GetValueID().GetId());
+ //~ for( list<ValueID>::iterator it = nodeInfo->m_values.begin(); it != nodeInfo->m_values.end(); ++it )
+ //~ {
+ //~ if( (*it) == _notification->GetValueID() )
+ //~ {
+ //~ nodeInfo->m_values.erase( it );
+ //~ break;
+ //~ }
+ //~ }
+ }
+ break;
+ }
+
+ /**< A node value has been updated from the Z-Wave network. */
+ case Notification::Type_ValueChanged:
+ /**< The associations for the node have changed. The application
+ should rebuild any group information it holds about the node. */
+ case Notification::Type_Group:
+ /**< A new node has been found (not already stored in zwcfg*.xml file) */
+ case Notification::Type_NodeNew:
+ /**< Basic node information has been receievd, such as whether the node is a
+ listening device, a routing device and its baud rate and basic, generic and
+ specific types. It is after this notification that you can call Manager::GetNodeType
+ to obtain a label containing the device description. */
+ case Notification::Type_NodeProtocolInfo:
+ /**< A node has triggered an event. This is commonly caused when a node sends
+ a Basic_Set command to the controller. The event value is stored in the notification. */
+ case Notification::Type_NodeEvent:
+ {
+ if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
+ {
+ // One of the node's association groups has changed
+ // TBD...
+ nodeInfo = nodeInfo;
+ }
+ break;
+ }
+
+
+ /**< A new node has been added to OpenZWave's list. This may be due
+ to a device being added to the Z-Wave network, or because
+ the application is initializing itself. */
+ case Notification::Type_NodeAdded:
+ {
+ // Add the new node to our list
+ NodeInfo* nodeInfo = new NodeInfo();
+ nodeInfo->m_homeId = _notification->GetHomeId();
+ nodeInfo->m_nodeId = _notification->GetNodeId();
+ nodeInfo->m_polled = false;
+ g_nodes.push_back( nodeInfo );
+ break;
+ }
+
+ /**< A node has been removed from OpenZWave's list. This may be due
+ to a device being removed from the Z-Wave network, or because the application is closing. */
+ case Notification::Type_NodeRemoved:
+ {
+ // Remove the node from our list
+ uint32 const homeId = _notification->GetHomeId();
+ uint8 const nodeId = _notification->GetNodeId();
+ for( list<NodeInfo*>::iterator it = g_nodes.begin(); it != g_nodes.end(); ++it )
+ {
+ NodeInfo* nodeInfo = *it;
+ if( ( nodeInfo->m_homeId == homeId ) && ( nodeInfo->m_nodeId == nodeId ) )
+ {
+ g_nodes.erase( it );
+ break;
+ }
+ }
+ break;
+ }
+
+ // Type_NodeNaming missing
+
+ /**< Polling of a node has been successfully turned off by a call to Manager::DisablePoll */
+ case Notification::Type_PollingDisabled:
+ {
+ if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
+ {
+ nodeInfo->m_polled = false;
+ }
+ break;
+ }
+
+ /**< Polling of a node has been successfully turned on by a call to Manager::EnablePoll */
+ case Notification::Type_PollingEnabled:
+ {
+ if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
+ {
+ nodeInfo->m_polled = true;
+ }
+ break;
+ }
+
+ /**< A driver for a PC Z-Wave controller has been added and is ready to use. The notification
+ will contain the controller's Home ID, which is needed to call most of the Manager methods. */
+ case Notification::Type_DriverReady:
+ {
+ g_homeId = _notification->GetHomeId();
+ break;
+ }
+
+ /**< Driver failed to load */
+ case Notification::Type_DriverFailed:
+ {
+ g_initFailed = true;
+ initCond.notify_all();
+ break;
+ }
+
+ //missing Type_DriverReset: < All nodes and values for this driver have been removed. This is sent instead of potentially hundreds of individual node and value notifications. */
+ //missing MsgComplete < The last message that was sent is now complete. */
+ //missing Type_EssentialNodeQueriesComplete, /**< The queries on a node that are essential to its operation have been completed. The node can now handle incoming messages. */
+ //missing Type_NodeQueriesComplete, /**< All the initialisation queries on a node have been completed. */
+
+ /*< All awake nodes have been queried, so client application can expected complete data for these nodes. */
+ case Notification::Type_AwakeNodesQueried:
+
+ /**< All nodes have been queried, so client application can expected complete data. */
+ case Notification::Type_AllNodesQueried:
+ {
+ initCond.notify_all();
+ break;
+ }
+
+ default:
+ {
+ }
+ }
+
+ // now we can send the captured event to STOMP queue
+ char *val1 = (char*) malloc(10); // notification type
+ char *val2 = (char*) malloc(6); // notification byte
+ char *val3 = (char*) malloc(14); // ValueID m_id and m_id1 as 64-bit integer
+ char *val4 = (char*) malloc(10); // ValueID._homeId
+ sprintf(val1, "0x%08x", _notification->GetType());
+ sprintf(val2, "0x%02x", _notification->GetByte());
+ sprintf(val3, "0x%10x", _notification->GetValueID().GetId()); // uint64 ValueID::GetId()const{ return (uint64) ( ( (uint64)m_id1 << 32 ) | m_id );}
+ sprintf(val4, "0x%08x", _notification->GetValueID().GetHomeId()); // uint32 GetHomeId()const{ return m_homeId; }
+ //
+ STOMP::hdrmap headers;
+ headers["NotificationType"] = val1;
+ headers["NotificationByte"] = val2;
+ headers["NotificationValueID"] = val3;
+ headers["NotificationValueHomeID"] = val4;
+ //
+ string empty = "" ;
+ stomp_client->send(*notifications_topic, headers, empty);
+ //
+ free(val1); free(val2); free(val3); free(val4);
+ //
+ g_criticalSection.unlock();
+}
+
+// ------------------------
+// THRIFT MAGIC
+// ------------------------
+// the Thrift-generated (and manually edited) RemoteManager implementation
+// for OpenZWave::Manager class
+#include "gen-cpp/RemoteManager_server.cpp"
+//
+
+int main(int argc, char **argv) {
+ // STOMP
+ stomp_client = new STOMP::PocoStomp("localhost", 61613);
+ stomp_client->connect();
+
+ // OpenZWave initialization
+ //initMutex.lock();
+
+ // Create the OpenZWave Manager.
+ // 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 log file will appear in the program's working directory.
+ Options::Create( "/home/ekarak/ozw/open-zwave-read-only/config/", "", "" );
+ Options::Get()->Lock();
+
+ Manager::Create();
+
+ // Add a callback handler to the manager. The second argument is a context that
+ // is passed to the OnNotification method. If the OnNotification is a method of
+ // a class, the context would usually be a pointer to that class object, to
+ // avoid the need for the notification handler to be a static.
+ Manager::Get()->AddWatcher( OnNotification, NULL );
+
+ // Add a Z-Wave Driver
+ // Modify this line to set the correct serial port for your PC interface.
+
+ string ozw_port = "/dev/ttyUSB0";
+
+ Manager::Get()->AddDriver( ozw_port );
+ //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.
+ // In a normal app, we would be handling notifications and building a UI for the user.
+ boost::unique_lock<boost::mutex> initLock(initMutex);
+ initCond.wait(initLock);
+
+ // initialize RemoteManager
+ int port = 9090;
+ shared_ptr<RemoteManagerHandler> handler(new RemoteManagerHandler());
+ shared_ptr<TProcessor> processor(new RemoteManagerProcessor(handler));
+ shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
+ shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
+ shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
+
+ TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
+ server.serve();
+ return 0;
+}
View
104 Makefile
@@ -0,0 +1,104 @@
+#
+# Makefile for OpenzWave Control Panel application
+# Greg Satz
+
+# GNU make only
+
+.SUFFIXES: .cpp .o .a .s .thrift
+
+CC := gcc
+CXX := g++
+LD := g++
+AR := ar rc
+RANLIB := ranlib
+THRIFT := thrift
+
+DEBUG_CFLAGS := -Wall -Wno-format -g -DDEBUG -Werror -O0
+RELEASE_CFLAGS := -Wall -Wno-unknown-pragmas -Wno-format -O3 -DNDEBUG
+
+DEBUG_LDFLAGS := -g
+
+# Change for DEBUG or RELEASE
+CFLAGS := -c $(DEBUG_CFLAGS)
+LDFLAGS := $(DEBUG_LDFLAGS)
+
+OPENZWAVE := ../open-zwave
+LIBTHRIFT := ../libmicrohttpd/src/daemon/.libs/libmicrohttpd.a
+
+INCLUDES := -I $(OPENZWAVE)/cpp/src -I $(OPENZWAVE)/cpp/src/command_classes/ \
+ -I $(OPENZWAVE)/cpp/src/value_classes/ -I $(OPENZWAVE)/cpp/src/platform/ \
+ -I $(OPENZWAVE)/cpp/src/platform/unix -I $(OPENZWAVE)/cpp/tinyxml/ \
+ -I /usr/local/include/thrift -I /home/ekarak/smc_6_0_1/lib/C++/ \
+ -I . -I gen-cpp/
+
+# Remove comment below for gnutls support
+GNUTLS := -lgnutls
+
+# for Linux uncomment out next two lines
+LIBZWAVE := $(wildcard $(OPENZWAVE)/cpp/lib/linux/*.a)
+LIBUSB := -ludev
+LIBPOCO := -lPocoNet -lboost_thread
+LIBTHRIFT := -lthrift
+
+# for Mac OS X comment out above 2 lines and uncomment next 2 lines
+#LIBZWAVE := $(wildcard $(OPENZWAVE)/cpp/lib/mac/*.a)
+#LIBUSB := -framework IOKit -framework CoreFoundation
+
+LIBS := $(LIBZWAVE) $(GNUTLS) $(LIBMICROHTTPD) $(LIBTHRIFT) $(LIBUSB) $(LIBPOCO)
+
+%.o : %.cpp
+ $(CXX) $(CFLAGS) $(INCLUDES) -o $@ $<
+
+%.o : %.c
+ $(CC) $(CFLAGS) $(INCLUDES) -o $@ $<
+
+all: main
+
+gen-cpp:
+ $(THRIFT) --gen cpp ozw.thrift
+
+gen-cpp/RemoteManager.o: gen-cpp/RemoteManager.cpp
+ g++ $(CFLAGS) -c gen-cpp/RemoteManager.cpp $(INCLUDES)
+
+gen-cpp/ozw_constants.o: gen-cpp/ozw_constants.cpp
+ g++ $(CFLAGS) -c gen-cpp/ozw_constants.cpp $(INCLUDES)
+
+gen-cpp/ozw_types.o: gen-cpp/ozw_types.cpp
+ g++ $(CFLAGS) -c gen-cpp/ozw_types.cpp $(INCLUDES)
+
+Stomp_sm.cpp: Stomp.sm
+ smc -c++ Stomp.sm
+
+Stomp_sm.o: Stomp_sm.cpp
+ g++ $(CFLAGS) -c Stomp_sm.cpp $(INCLUDES)
+
+StompSocket.o: StompSocket.cpp StompSocket.h
+ g++ $(CFLAGS) -c StompSocket.cpp $(INCLUDES)
+
+PocoStomp.o: Stomp_sm.cpp StompSocket.o
+ g++ $(CFLAGS) -c PocoStomp.cpp $(INCLUDES)
+
+#~ ozwcp.o: ozwcp.h webserver.h $(OPENZWAVE)/cpp/src/Options.h $(OPENZWAVE)/cpp/src/Manager.h \
+ #~ $(OPENZWAVE)/cpp/src/Node.h $(OPENZWAVE)/cpp/src/Group.h \
+ #~ $(OPENZWAVE)/cpp/src/Notification.h $(OPENZWAVE)/cpp/src/platform/Log.h \
+ #~ Stomp_sm.cpp
+
+#~ webserver.o: webserver.h ozwcp.h $(OPENZWAVE)/cpp/src/Options.h $(OPENZWAVE)/cpp/src/Manager.h \
+ #~ $(OPENZWAVE)/cpp/src/Node.h $(OPENZWAVE)/cpp/src/Group.h \
+ #~ $(OPENZWAVE)/cpp/src/Notification.h $(OPENZWAVE)/cpp/src/platform/Log.h
+
+#~ ozwcp: ozwcp.o webserver.o zwavelib.o Stomp_sm.o StompSocket.o PocoStomp.o
+ #~ $(LD) -o $@ $(LDFLAGS) ozwcp.o webserver.o zwavelib.o Stomp_sm.o StompSocket.o PocoStomp.o $(LIBS)
+
+Main.o: Main.cpp Stomp_sm.o gen-cpp/RemoteManager_server.cpp
+ g++ $(CFLAGS) -c Main.cpp $(INCLUDES)
+
+main: Main.o Stomp_sm.o StompSocket.o PocoStomp.o gen-cpp/RemoteManager.o gen-cpp/ozw_constants.o gen-cpp/ozw_types.o
+ $(LD) -o $@ $(LDFLAGS) Main.o Stomp_sm.o StompSocket.o PocoStomp.o gen-cpp/RemoteManager.o gen-cpp/ozw_constants.o gen-cpp/ozw_types.o $(LIBS)
+
+dist: main
+ rm -f Ansible_OpenZWave.tar.gz
+ tar -c --exclude=".svn" -hvzf Ansible_OpenZWave.tar.gz ozwcp config/ cp.html cp.js openzwavetinyicon.png README
+
+clean:
+ rm -f main *.o Stomp_sm.*
View
13 OZW_README.txt
@@ -0,0 +1,13 @@
+homeId = 0x00006258
+
+rvid = OpenZWave::RemoteValueID.new
+rvid._homeId = homeId
+rvid._nodeId = 2
+rvid._genre = OpenZWave::RemoteValueGenre::ValueGenre_Basic
+rvid._type = OpenZWave::RemoteValueType::ValueType_Bool
+rvid._instance = 0
+rvid._valueIndex = 0
+rvid._commandClassId = 0
+
+
+OZWmgr.SwitchNodeOn(homeId, 2)
View
401 PocoStomp.cpp
@@ -0,0 +1,401 @@
+//-----------------------------------------------------------------------------
+//
+// PocoStomp.cpp
+//
+// a STOMP (Simple Text Oriented Messaging Protocol) client for OZW
+// using the Poco library for platform interoperability
+//
+// Copyright (c) 2011 Elias Karakoulakis
+//
+// SOFTWARE NOTICE AND LICENSE
+//
+// This file is part of OpenZWave.
+//
+// OpenZWave is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// OpenZWave is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with OpenZWave. If not, see <http://www.gnu.org/licenses/>.
+//
+//-----------------------------------------------------------------------------
+
+#include <stdlib.h>
+#include <iostream>
+
+#include "PocoStomp.h"
+
+using namespace std;
+
+void hexdump(const void *ptr, int buflen) {
+ unsigned char *buf = (unsigned char*)ptr;
+ int i, j;
+ for (i=0; i<buflen; i+=16) {
+ printf("%06x: ", i);
+ for (j=0; j<16; j++)
+ if (i+j < buflen)
+ printf("%02x ", buf[i+j]);
+ else
+ printf(" ");
+ printf(" ");
+ for (j=0; j<16; j++)
+ if (i+j < buflen)
+ printf("%c", isprint(buf[i+j]) ? buf[i+j] : '.');
+ printf("\n");
+ }
+}
+
+namespace STOMP {
+
+// ####################################################
+// constructor
+// ####################################################
+PocoStomp::PocoStomp(const std::string& hostname, int port):
+ m_connection(new Connection),
+ m_ackmode(ACK_AUTO),
+ m_fsm(* this) // initialize the state machine
+{
+ // insert valid server commands in set
+ m_stomp_server_commands.insert("CONNECTED");
+ m_stomp_server_commands.insert("MESSAGE");
+ m_stomp_server_commands.insert("RECEIPT");
+ m_stomp_server_commands.insert("ERROR");
+ // our custom thread error handler
+ MyErrorHandler eh;
+ //Poco::ErrorHandler* pOldEH = Poco::ErrorHandler::set(&eh);
+ Poco::ErrorHandler::set(&eh);
+ //
+ m_initcond_mutex = new Poco::Mutex();
+ m_initcond = new Poco::Condition();
+ m_mutex = new Poco::Mutex();
+ (m_thread = new Poco::Thread())->start(*this);
+ m_connection->addr = new Poco::Net::SocketAddress(hostname, port);
+ m_connection->socket = new Poco::Net::StompSocket(*(m_connection->addr));
+ // TODO: check connection
+ // signal FSM that we're connected
+ m_fsm.socket_connected();
+}
+
+// ####################################################
+//destructor
+// ####################################################
+PocoStomp::~PocoStomp()
+{
+ //TODO: signal thread to stop
+ m_thread->join(1000);
+ delete(m_thread);
+ delete(m_connection);
+ delete(m_mutex);
+ delete(m_initcond);
+ delete(m_initcond_mutex);
+}
+
+// ####################################################
+bool PocoStomp::connect()
+// ####################################################
+{
+ bool result=true;
+ m_mutex->lock();
+ if (m_fsm.getState().getId() == StompFSM_map::SocketConnected.getId()) {
+ //std::cout << "Sending CONNECT frame...";
+ Frame _frame("CONNECT");
+ //
+ if (stomp_write(&_frame)) {
+ m_fsm.send_frame(&_frame);
+ }
+ //
+ }
+ m_mutex->unlock();
+ return(result);
+}
+
+
+// ####################################################
+bool PocoStomp::subscribe (std::string& _topic, pfnOnStompMessage_t _callback)
+// ####################################################
+{
+ bool result = true;
+ //std::cout << "Sending SUBSCRIBE...";
+ Frame _frame("SUBSCRIBE");
+ _frame.headers["destination"] = _topic;
+ //
+ m_mutex->lock();
+ {
+ m_sendqueue.push(new Frame(_frame));
+ m_subscriptions[_topic] = _callback;
+ }
+ m_mutex->unlock();
+ return(result);
+}
+
+// ####################################################
+bool PocoStomp::send ( std::string& _topic, std::map<std::string, std::string> _headers, std::string& _body)
+// ####################################################
+{
+ bool result = true;
+ Frame _frame("SEND");
+ _frame.headers = _headers;
+ _frame.body = _body;
+ _frame.headers["destination"] = _topic;
+ //
+ m_mutex->lock();
+ //
+ m_sendqueue.push(new Frame(_frame));
+ //
+ m_mutex->unlock();
+ return(result);
+}
+
+
+//incoming frame by STOMP server, notify all callbacks for this topic (header: "destination")
+// ####################################################
+void PocoStomp::notify_callbacks(PFrame _frame)
+// ####################################################
+{
+ //get destination topic
+ if (_frame->headers.find("destination") != _frame->headers.end()) {
+ std::string* dest = new std::string(_frame->headers["destination"]);
+ //std::cout << "notify_callbacks dest=" << dest << std::endl;
+ //
+ m_mutex->lock();
+ pfnOnStompMessage_t callback_function = m_subscriptions[*dest];
+ m_mutex->unlock();
+ //
+ if (callback_function != NULL) {
+ callback_function(_frame);
+ }
+ //
+ delete(dest);
+ }
+}
+
+//
+// ####################################################
+bool PocoStomp::stomp_read(PFrame *frame)
+// ####################################################
+{
+ int bytes_read; //length,
+ const char* str;
+ PFrame newframe;
+ std::string line, key, val;
+ size_t pos;
+ // scan all incoming data for frames
+ while (m_connection->socket->incoming_data_waiting()) {
+ //~ std::cout << "stomp_read awoken! ";
+ // Step 1. Parse the server command, skipping past uninteresting lines
+ if (m_connection->socket->receiveMessage(line)) {
+ //~ std::cout << "line=" << line << "(" << line.length() << ")" << std::endl;
+ //~ hexdump(line.c_str(), line.length());
+ // check for valid command
+ if (m_stomp_server_commands.find(line) != m_stomp_server_commands.end()) {
+ str = line.c_str();
+ //std::cout << endl << line.length() << " bytes in command:\n";
+ //hexdump(line.c_str(), line.length());
+ newframe = new Frame(line);
+
+ // Step 2. Parse the headers.
+ while( 1 ) {
+ if (m_connection->socket->receiveMessage(line)) {
+ //std::cout << endl << line.length() << " bytes in header:\n ";
+ //hexdump(line.c_str(), line.length());
+ // Done with headers? (empty line separating headers and body?)
+ if (line.length() == 0) {
+ //cout << m_connection->socket->available() << " bytes still in receive queue" << endl;
+ break;
+ }
+ if ((pos = line.find(":")) == line.npos) throw("stomp_read(): no colon delimiter in header!");
+ // extract key, value from header line
+ key = line.substr(0, pos);
+ val = line.substr(pos+1);
+ //std::cout << "header key=" << key << " value=" << val << endl;
+ // Insert key/value into hash table.
+ newframe->headers[key] = val;
+ } else {
+ break;
+ }
+ }
+
+ // Step 3. Read body
+ char *body;
+ int bodylen;
+ if (newframe->headers.find("content-length") != newframe->headers.end()) {
+ std::stringstream(newframe->headers["content-length"]) >> bodylen;
+ if (bodylen > 0) {
+ body = (char*) malloc(bodylen+1);
+ // NOTE: must use receiveRawBytes
+ bytes_read = m_connection->socket->receiveRawBytes(body, bodylen+1);
+ //~ std::cout << "stomp_read(): body by content-length: " << bodylen+1 << endl;
+ //~ hexdump(body, bytes_read);
+ if (bytes_read != bodylen+1) throw("stomp_read(): body shorter than content-length");
+ newframe->body = std::string(body);
+ free(body);
+ } else {
+ //~ std::cout << "\nempty body!\n";
+ }
+ } else {
+ // no content-length, read all available bytes till \0
+ m_connection->socket->receiveMessage(line, '\0');
+ if (line.length() > 0) {
+ //~ std::cout << "stomp_read(): " << line.length() << " bytes in body:\n";
+ //~ hexdump(line.c_str(), line.length());
+ newframe->body = line;
+ } else {
+ //~ std::cout << "\nempty body!\n";
+ }
+ }
+ (*frame) = newframe;
+ return(true);
+ }
+ }
+ }
+ return(false);
+
+}
+
+
+// ####################################################
+bool PocoStomp::stomp_write(PFrame frame)
+// ####################################################
+{
+ // step 1. Write the command.
+// const char* s;
+ //int len;
+ if (frame->command.length() > 0) {
+ stomp_write_buffer(frame->command);
+ stomp_write_buffer("\n", 1);
+ } else {
+ throw("stomp_write: command not set!!");
+ }
+ // step 2. Write the headers
+ if( frame->headers.size() > 0) {
+ string key, value;
+ hdrmap::iterator it;
+ for ( it=frame->headers.begin() ; it != frame->headers.end(); it++ ) {
+ key = (*it).first;
+ value = (*it).second;
+ stomp_write_buffer(key);
+ stomp_write_buffer(":", 1);
+ stomp_write_buffer(value);
+ stomp_write_buffer("\n", 1);
+ }
+ }
+ // special header: content-length
+ if(frame->body.length() > 0) {
+ std::stringstream length_string;
+ length_string << frame->body.length();
+ stomp_write_buffer("content-length:", 15);
+ stomp_write_buffer(length_string.str());
+ stomp_write_buffer("\n", 1);
+ }
+ // write newline signifying end of headers
+ stomp_write_buffer("\n", 1);
+
+ // step 3. Write the body
+ if( frame->body.length() > 0) {
+ stomp_write_buffer(frame->body);
+ }
+
+ // write terminating NULL char
+ stomp_write_buffer("\0", 1);
+ return (true);
+}
+
+// ####################################################
+bool PocoStomp::stomp_write_buffer(const char *data, int size)
+// ####################################################
+{
+ int remaining = size;
+ int sent = 0;
+ size=0;
+ while( remaining>0 ) {
+ int length = remaining;
+ sent = m_connection->socket->sendBytes(data,length);
+ data += sent;
+ remaining -= sent;
+ if( length != sent) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// ####################################################
+bool PocoStomp::stomp_write_buffer(std::string s)
+// ####################################################
+{
+ return(stomp_write_buffer(s.c_str(), s.length()));
+}
+
+// ####################################################
+void PocoStomp::debug_print(std::string message)
+{
+ std::cout << "FSM DEBUG: " << message << endl;
+}
+
+void PocoStomp::initialized()
+{
+ m_initcond->broadcast();
+}
+
+void PocoStomp::start_timer(PocoStompState* _state)
+{
+}
+
+void PocoStomp::stop_timer()
+{
+}
+
+
+// PocoStomp::run(): Main worker thread, watches the stomp socket for
+// subscription MESSAGEs, sends outgoing messages
+// ####################################################
+void PocoStomp::run()
+// ####################################################
+{
+ PFrame _frame;
+ bool cond1, cond2;
+ int state_id;
+ //wait for stomp client initialization
+ m_initcond_mutex->lock();
+ m_initcond->wait(*m_initcond_mutex);
+ m_initcond_mutex->unlock();
+ // ok, stomp object is initialized, lets start.
+ for (;;) {
+ //
+ m_mutex->lock();
+ //
+ // phase 1: read incoming frames, if any
+ if (stomp_read(&_frame)) {
+ m_fsm.receive_frame(_frame);
+ //std::cout << "received frame:" << _frame->command << std::endl;
+ free(_frame);
+ }
+ //
+ // phase 2: send first frame in queue, if any
+ state_id = m_fsm.getState().getId();
+ cond1 = (state_id == StompFSM_map::Ready.getId());
+ cond2 = (m_sendqueue.size() > 0);
+ if (cond1 && cond2) {
+ _frame = m_sendqueue.front();
+ m_sendqueue.pop();
+ if (stomp_write(_frame)) {
+ m_fsm.send_frame(_frame);
+ //std::cout << "sent frame:" << _frame->command << std::endl;
+ }
+ free(_frame);
+ }
+ //
+ m_mutex->unlock();
+ //
+ Poco::Thread::sleep(50);
+ }
+}
+
+
+}
View
193 PocoStomp.h
@@ -0,0 +1,193 @@
+//-----------------------------------------------------------------------------
+//
+// PocoStomp.h
+//
+// a STOMP (Simple Text Oriented Messaging Protocol) client for OZW
+// using the Poco library for platform interoperability
+//
+// Copyright (c) 2011 Elias Karakoulakis
+//
+// SOFTWARE NOTICE AND LICENSE
+//
+// This file is part of OpenZWave.
+//
+// OpenZWave is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// OpenZWave is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with OpenZWave. If not, see <http://www.gnu.org/licenses/>.
+//
+//-----------------------------------------------------------------------------
+#ifndef __POCOSTOMP_H_
+#define __POCOSTOMP_H_
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <queue>
+#include <map>
+#include <set>
+
+#include "Poco/Net/SocketAddress.h"
+#include "Poco/Net/Socket.h"
+#include "Poco/HashMap.h"
+#include "Poco/Mutex.h"
+#include "Poco/Runnable.h"
+#include "Poco/Thread.h"
+#include "Poco/Timespan.h"
+#include "Poco/Condition.h"
+#include "Poco/ErrorHandler.h"
+
+#include "StompSocket.h"
+
+// helpers
+namespace STOMP {
+
+ // a STOMP connection
+ typedef struct {
+ Poco::Net::SocketAddress* addr;
+ Poco::Net::StompSocket* socket;
+ } Connection;
+
+ typedef std::map<std::string, std::string> hdrmap;
+
+ // a STOMP frame
+ class Frame {
+ public:
+ std::string command;
+ hdrmap headers;
+ std::string body;
+ Frame(std::string cmd) : command(cmd) {};
+ Frame(std::string cmd, hdrmap h) : command(cmd), headers(h) {};
+ Frame(std::string cmd, hdrmap h, std::string b) : command(cmd), headers(h), body(b) {};
+ Frame(Frame& other) {
+ command = other.command;
+ headers = other.headers;
+ body = other.body;
+ };
+ };
+ typedef Frame* PFrame;
+
+ typedef enum {
+ ACK_AUTO=0, // implicit acknowledgment (no ACK is sent)
+ ACK_CLIENT // explicit acknowledgment (must ACK)
+ } AckMode;
+
+ typedef struct data_block_list {
+ char data[1024];
+ struct data_block_list *next;
+ } data_block_list;
+
+}
+// the state machine must be included here (uses the structs above)
+#include "Stomp_sm.h"
+
+template <class T>
+std::string to_string(T t, std::ios_base & (*f)(std::ios_base&))
+{
+ std::ostringstream oss;
+ oss << f << t;
+ return oss.str();
+}
+
+//
+
+
+namespace STOMP {
+
+ typedef void (*pfnOnStompMessage_t)( Frame* _frame );
+
+ class MyErrorHandler: public Poco::ErrorHandler
+ {
+ public:
+ void exception(const Poco::Exception& exc)
+ {
+ std::cerr << exc.displayText() << std::endl;
+ }
+ void exception(const std::exception& exc)
+ {
+ std::cerr << exc.what() << std::endl;
+ }
+ void exception()
+ {
+ std::cerr << "unknown exception" << std::endl;
+ }
+ };
+
+ class PocoStomp: public Poco::Runnable
+ {
+ friend class StompFSM_map_Disconnected;
+ friend class StompFSM_map_SocketConnected;
+ friend class StompFSM_map_WaitingConnectionAck;
+ friend class StompFSM_map_Ready;
+ friend class StompFSM_map_ReceivingFrame;
+ friend class StompFSM_map_SendingAck;
+ friend class StompFSM_map_WaitingCommandAck;
+ friend class StompFSM_map_Disconnecting;
+
+ //instance variables
+ protected:
+ Connection* m_connection;
+ AckMode m_ackmode;
+ StompContext m_fsm;
+ //
+
+ Poco::Thread* m_thread;
+ Poco::Mutex* m_mutex;
+ Poco::Mutex* m_initcond_mutex;
+ Poco::Condition* m_initcond;
+ std::queue<PFrame> m_sendqueue;
+ std::map<std::string, pfnOnStompMessage_t> m_subscriptions;
+ //
+ //#########################
+ //---- FILE: Stomp.cpp ----
+ //#########################
+ public:
+ //initialization condition
+ //~ apr_thread_cond_t* m_initcond;
+ //~ apr_thread_mutex_t* m_initcond_mutex;
+ //
+ PocoStomp(const std::string& hostname, int port);
+ ~PocoStomp();
+ // thread-safe methods called from outside the thread loop
+ bool connect ();
+ bool subscribe ( std::string& _topic, pfnOnStompMessage_t callback);
+ bool send ( std::string& _topic, hdrmap _headers, std::string& _body);
+ //
+ PocoStompState& get_state() { return m_fsm.getState(); };
+ AckMode get_ackmode() { return m_ackmode; };
+ //
+ protected:
+ //######################################
+ //---- FILE: Stomp_fsmfunctions.cpp ----
+ //######################################
+ void initialized();
+ void start_timer(PocoStompState* _state);
+ void stop_timer();
+ void debug_print(std::string message);
+ void notify_callbacks(Frame* _frame);
+
+ // IO methods called from the thread loop
+ //
+ bool stomp_read(STOMP::PFrame *frame);
+ bool stomp_write(STOMP::PFrame frame);
+ bool stomp_write_buffer(const char *data, int size);
+ bool stomp_write_buffer(std::string s) ;
+
+ // more methods called from the thread loop
+ bool socket_connect(const std::string& hostname, int port);
+ bool incoming_data_waiting();
+ private:
+ void run(); // Runnable::run(): Stomp worker thread implementation
+ std::set<std::string> m_stomp_server_commands;
+ }; //class
+
+}
+#endif
View
171 Stomp.sm
@@ -0,0 +1,171 @@
+%{
+//-----------------------------------------------------------------------------
+//
+// Project: STOMP for OpenZWave
+//
+// a simple STOMP (Simple Text Oriented Messaging Protocol) client
+// for more info on the protocol, see URL: http://stomp.github.com/
+//
+// 1) subscribes to STOMP queues to pass ZWave commands to OpenZWave
+// 2) sends to STOMP queues ZWave notifications by OpenZWave's Notification mechanism
+//
+// Copyright (c) 2011 Elias Karakoulakis
+//
+// SOFTWARE NOTICE AND LICENSE
+//
+// This file is part of OpenZWave.
+//
+// OpenZWave is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// OpenZWave is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with OpenZWave. If not, see <http://www.gnu.org/licenses/>.
+//
+//-----------------------------------------------------------------------------
+%}
+
+%class PocoStomp
+%header PocoStomp.h
+%include PocoStomp.h
+
+%package STOMP
+
+%start StompFSM_map::Disconnected
+%map StompFSM_map
+%%
+
+// ############
+Disconnected
+{
+ socket_connected
+ SocketConnected {
+ debug_print("socket connected!\n");
+ }
+}
+
+// ############
+SocketConnected
+{
+ send_frame(_frame: PFrame) [
+ (_frame->command.compare("CONNECT") == 0)
+ ] Ready {
+ initialized(); // signal I/O thread to start
+ debug_print("CONNECT");
+ }
+}
+
+// ############
+Ready
+{
+// -------- Sending frames (client->server)---------
+// DISCONNECT
+ send_frame(_frame: PFrame) [
+ (_frame->command.compare("DISCONNECT") == 0)
+ ] Disconnecting {
+ start_timer(&StompFSM_map::Disconnecting);
+ debug_print("Disconnecting...\n");
+ }
+// SUBSCRIBE
+ send_frame(_frame: PFrame) [
+ (_frame->command.compare("SUBSCRIBE") == 0)
+ ] Ready {
+ debug_print("SUBSCRIBE");
+ }
+// other command frames (client->server),
+ send_frame(_frame: PFrame)
+ Ready {
+ debug_print("StompClient: send_frame\n");
+ }
+// -------- Receiving frames (server->client)---------
+// CONNECTED
+ receive_frame(_frame: PFrame) [
+ (_frame->command.compare("CONNECTED") == 0)
+ ] Ready {
+ debug_print("CONNECTED!\n");
+ }
+// MESSAGE frame, ack: AUTO
+ receive_frame(_frame: PFrame) [
+ (_frame->command.compare("MESSAGE") == 0)
+ &&
+ (ctxt.get_ackmode() == ACK_AUTO)
+ ] Ready {
+ debug_print("MESSAGE frame from STOMP server!\n");
+ notify_callbacks(_frame);
+ }
+// MESSAGE frame, ack: CLIENT
+ receive_frame(_frame: PFrame) [
+ (_frame->command.compare("MESSAGE") == 0)
+ &&
+ (ctxt.get_ackmode() == ACK_CLIENT)
+ ] SendingAck {
+ debug_print("MESSAGE frame from STOMP server!\n");
+ notify_callbacks(_frame);
+ }
+// RECEIPT frame, ack: AUTO
+ receive_frame(_frame: PFrame) [
+ (_frame->command.compare("RECEIPT") == 0)
+ &&
+ (ctxt.get_ackmode() == ACK_AUTO)
+ ] Ready {
+ debug_print("RECEIPT, ack:AUTO\n");
+ }
+// RECEIPT frame, ack: CLIENT
+ receive_frame(_frame: PFrame) [
+ (_frame->command.compare("RECEIPT") == 0)
+ &&
+ (ctxt.get_ackmode() == ACK_CLIENT)
+ ] SendingAck {
+ debug_print("RECEIPT, ack:CLIENT\n");
+ }
+// ERROR frame
+ receive_frame(_frame: PFrame) [
+ (_frame->command.compare("ERROR") == 0)
+ ] Ready {
+ debug_print("ERROR frame from STOMP server!\n");
+ }
+// ------------------------
+ timeout Ready {
+ debug_print("timeout waiting for full STOMP frame!");
+ }
+
+}
+
+// ############
+SendingAck
+{
+ send_frame(_frame: PFrame) [
+ (_frame->command.compare("ACK") == 0)
+ &&
+ (ctxt.get_ackmode() == ACK_CLIENT)
+ ] Ready {
+
+ }
+}
+
+// ############
+Disconnecting
+{
+ timeout(waitingfor: PocoStompState*)[
+ strcmp(waitingfor->getName(), "Disconnecting")==0
+ ]
+ Disconnected {
+ debug_print("ooops, timed out disconnecting!!");
+ }
+
+ ack_received(_frame: PFrame) [
+ _frame->command.compare("DISCONNECT")==0
+ ]
+ Disconnected {
+ stop_timer();
+ debug_print("ACK received!");
+ }
+
+}
+%%
View
247 StompSocket.cpp
@@ -0,0 +1,247 @@
+//
+// StompSocket.cpp
+//
+// (c) Elias Karakoulakis: derived from DialogSocket.cpp,
+// a PoCo socket to be used for STOMP
+// the main difference is the use of LF instead of CR/LF for line termination
+// and the use of NULL (\0) to terminate each frame
+//
+// Library: Net
+// Package: Sockets
+// Module: StompSocket
+//
+// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
+// and Contributors.
+//
+// Permission is hereby granted, free of charge, to any person or organization
+// obtaining a copy of the software and accompanying documentation covered by
+// this license (the "Software") to use, reproduce, display, distribute,
+// execute, and transmit the Software, and to prepare derivative works of the
+// Software, and to permit third-parties to whom the Software is furnished to
+// do so, all subject to the following:
+//
+// The copyright notices in the Software and this entire statement, including
+// the above license grant, this restriction and the following disclaimer,
+// must be included in all copies of the Software, in whole or in part, and
+// all derivative works of the Software, unless such copies or derivative
+// works are solely in the form of machine-executable object code generated by
+// a source language processor.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+
+#include "StompSocket.h"
+#include "Ascii.h"
+#include <cstring>
+
+
+namespace Poco {
+namespace Net {
+
+
+StompSocket::StompSocket():
+ _pBuffer(0),
+ _pNext(0),
+ _pEnd(0)
+{
+ allocBuffer();
+}
+
+
+StompSocket::StompSocket(const SocketAddress& address):
+ StreamSocket(address),
+ _pBuffer(0),
+ _pNext(0),
+ _pEnd(0)
+{
+ allocBuffer();
+}
+
+
+StompSocket::StompSocket(const Socket& socket):
+ StreamSocket(socket),
+ _pBuffer(0),
+ _pNext(0),
+ _pEnd(0)
+{
+ allocBuffer();
+}
+
+
+StompSocket::~StompSocket()
+{
+ delete [] _pBuffer;
+}
+
+
+StompSocket& StompSocket::operator = (const Socket& socket)
+{
+ StreamSocket::operator = (socket);
+ _pNext = _pBuffer;
+ _pEnd = _pBuffer;
+ return *this;
+}
+
+
+StompSocket& StompSocket::operator = (const StompSocket& socket)
+{
+ StreamSocket::operator = (socket);
+ _pNext = _pBuffer;
+ _pEnd = _pBuffer;
+ return *this;
+}
+
+
+void StompSocket::sendByte(unsigned char ch)
+{
+ sendBytes(&ch, 1);
+}
+
+
+void StompSocket::sendString(const char* str)
+{
+ sendBytes(str, (int) std::strlen(str));
+}
+
+
+void StompSocket::sendString(const std::string& str)
+{
+ sendBytes(str.data(), (int) str.length());
+}
+
+
+void StompSocket::sendMessage(const std::string& message)
+{
+ std::string line;
+ line.reserve(message.length() + 1);
+ line.append(message);
+ line.append("\n");
+ sendString(line);
+}
+
+
+void StompSocket::sendMessage(const std::string& message, const std::string& arg)
+{
+ std::string line;
+ line.reserve(message.length() + arg.length() + 2);
+ line.append(message);
+ if (!arg.empty())
+ {
+ line.append(" ");
+ line.append(arg);
+ }
+ line.append("\n");
+ sendString(line);
+}
+
+
+void StompSocket::sendMessage(const std::string& message, const std::string& arg1, const std::string& arg2)
+{
+ std::string line;
+ line.reserve(message.length() + arg1.length() +arg2.length() + 3);
+ line.append(message);
+ line.append(" ");
+ line.append(arg1);
+ if (!arg2.empty())
+ {
+ line.append(" ");
+ line.append(arg2);
+ }
+ line.append("\n");
+ sendString(line);
+}
+
+
+bool StompSocket::receiveMessage(std::string& message, char terminator)
+{
+ message.clear();
+ return receiveLine(message, terminator);
+}
+
+
+int StompSocket::get()
+{
+ refill();
+ //std::cout << "get()==" << *_pNext << "(" << ((int) *_pNext) << ")" << std::endl;
+ if (_pNext != _pEnd)
+ return std::char_traits<char>::to_int_type(*_pNext++);
+ else
+ return EOF_CHAR;
+}
+
+
+int StompSocket::peek()
+{
+ refill();
+ if (_pNext != _pEnd)
+ return std::char_traits<char>::to_int_type(*_pNext);
+ else
+ return EOF_CHAR;
+}
+
+
+void StompSocket::refill()
+{
+ if (_pNext == _pEnd)
+ {
+ int n = receiveBytes(_pBuffer, RECEIVE_BUFFER_SIZE);
+ _pNext = _pBuffer;
+ _pEnd = _pBuffer + n;
+ //~ std::cout << "refill() read "<< n <<" bytes:'" << std::string(_pBuffer) << "'" << std::endl;
+ }
+}
+
+
+void StompSocket::allocBuffer()
+{
+ _pBuffer = new char [RECEIVE_BUFFER_SIZE];
+ _pNext = _pBuffer;
+ _pEnd = _pBuffer;
+}
+
+
+bool StompSocket::receiveLine(std::string& line, char terminator)
+{
+ // An old wisdom goes: be strict in what you emit
+ // and generous in what you accept.
+ int ch = get();
+ while (ch != EOF_CHAR && ch != terminator)
+ {
+ line += (char) ch;
+ ch = get();
+ }
+
+ if (ch == EOF_CHAR)
+ return false;
+ return true;
+}
+
+
+int StompSocket::receiveRawBytes(void* buffer, int length)
+{
+ refill();
+ int n = static_cast<int>(_pEnd - _pNext);
+ if (n > length) n = length;
+ std::memcpy(buffer, _pNext, n);
+ _pNext += n;
+ return n;
+}
+
+bool StompSocket::incoming_data_waiting()
+{
+ if (_pNext == _pEnd) {
+ //_pBuffer doesn't have any new data
+ // return(poll(Poco::Timespan(), Poco::Net::Socket::SELECT_READ));
+ return(available()>0);
+ } else
+ return(true);
+}
+
+} } // namespace Poco::Net
View
174 StompSocket.h
@@ -0,0 +1,174 @@
+//
+// StompSocket.h
+//
+// $Id: //poco/1.4/Net/include/Poco/Net/StompSocket.h#1 $
+//
+// Library: Net
+// Package: Sockets
+// Module: StompSocket
+//
+// Definition of the StompSocket class.
+//
+// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
+// and Contributors.
+//
+// Permission is hereby granted, free of charge, to any person or organization
+// obtaining a copy of the software and accompanying documentation covered by
+// this license (the "Software") to use, reproduce, display, distribute,
+// execute, and transmit the Software, and to prepare derivative works of the
+// Software, and to permit third-parties to whom the Software is furnished to
+// do so, all subject to the following:
+//
+// The copyright notices in the Software and this entire statement, including
+// the above license grant, this restriction and the following disclaimer,
+// must be included in all copies of the Software, in whole or in part, and
+// all derivative works of the Software, unless such copies or derivative
+// works are solely in the form of machine-executable object code generated by
+// a source language processor.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+
+#ifndef Net_StompSocket_INCLUDED
+#define Net_StompSocket_INCLUDED
+
+
+#include "Poco/Net/Net.h"
+#include "Poco/Net/StreamSocket.h"
+
+#include <string>
+#include <sstream>
+#include <iostream>
+
+namespace Poco {
+namespace Net {
+
+
+class Net_API StompSocket: public StreamSocket
+ /// StompSocket is a subclass of StreamSocket that
+ /// can be used for implementing request-response
+ /// based client server STOMP connections.
+ ///
+ /// A request is always a single-line command terminated
+ /// by LF.
+ ///
+ ///
+ /// Warning: Do not call receiveBytes() on a StompSocket.
+ /// Due to internal buffering in StompSocket, receiveBytes()
+ /// may return an unexpected result and interfere with
+ /// StompSocket's buffering. Use receiveRawBytes() instead.
+{
+public:
+ StompSocket();
+ /// Creates an unconnected stream socket.
+ ///
+ /// Before sending or receiving data, the socket
+ /// must be connected with a call to connect().
+
+ explicit StompSocket(const SocketAddress& address);
+ /// Creates a stream socket and connects it to
+ /// the socket specified by address.
+
+ StompSocket(const Socket& socket);
+ /// Creates the StompSocket with the SocketImpl
+ /// from another socket. The SocketImpl must be
+ /// a StreamSocketImpl, otherwise an InvalidArgumentException
+ /// will be thrown.
+
+ ~StompSocket();
+ /// Destroys the StompSocket.
+
+ StompSocket& operator = (const Socket& socket);
+ /// Assignment operator.
+ ///
+ /// Releases the socket's SocketImpl and
+ /// attaches the SocketImpl from the other socket and
+ /// increments the reference count of the SocketImpl.
+
+ StompSocket& operator = (const StompSocket& socket);
+ /// Assignment operator.
+
+ void sendByte(unsigned char ch);
+ /// Sends a single byte over the socket connection.
+
+ void sendString(const char* str);
+ /// Sends the given null-terminated string over
+ /// the socket connection.
+
+ void sendString(const std::string& str);
+ /// Sends the given string over the socket connection.
+
+ void sendMessage(const std::string& message);
+ /// Appends a CR-LF sequence to the message and sends it
+ /// over the socket connection.
+
+ void sendMessage(const std::string& message, const std::string& arg);
+ /// Concatenates message and arg, separated by a space, appends a
+ /// CR-LF sequence, and sends the result over the socket connection.
+
+ void sendMessage(const std::string& message, const std::string& arg1, const std::string& arg2);
+ /// Concatenates message and args, separated by a space, appends a
+ /// CR-LF sequence, and sends the result over the socket connection.
+
+ bool receiveMessage(std::string& message, char terminator='\n');
+ /// Receives a single-line message, terminated by LF,
+ /// from the socket connection and appends it to response.
+ ///
+ /// Returns true if a message has been read or false if
+ /// the connection has been closed by the peer.
+
+ int get();
+ /// Reads one character from the connection.
+ ///
+ /// Returns -1 (EOF_CHAR) if no more characters are available.
+
+ int peek();
+ /// Returns the character that would be returned by the next call
+ /// to get(), without actually extracting the character from the
+ /// buffer.
+ ///
+ /// Returns -1 (EOF_CHAR) if no more characters are available.
+
+ int receiveRawBytes(void* buffer, int length);
+ /// Read up to length bytes from the connection and place
+ /// them into buffer. If there are data bytes in the internal
+ /// buffer, these bytes are returned first.
+ ///
+ /// Use this member function instead of receiveBytes().
+ ///
+ /// Returns the number of bytes read, which may be
+ /// less than requested.
+
+ bool incoming_data_waiting();
+ /// ekarak: return true if there are waiting data in buffer or socket
+
+protected:
+ void allocBuffer();
+ void refill();
+ bool receiveLine(std::string& line, char terminator);
+
+private:
+ enum
+ {
+ RECEIVE_BUFFER_SIZE = 1024,
+ EOF_CHAR = -1
+ };
+
+ char* _pBuffer;
+ char* _pNext;
+ char* _pEnd;
+
+};
+
+
+} } // namespace Poco::Net
+
+
+#endif // Net_StompSocket_INCLUDED
View
195 create_server.rb
@@ -0,0 +1,195 @@
+# a Thrift server generator for OpenZWave
+# transform a server skeleton file into a fully operational server
+# a.k.a. "fills in the blanks for you"
+#
+# (c) 2011 Elias Karakoulakis <elias.karakoulakis@gmail.com>
+#
+require 'rubygems'
+require 'rbgccxml'
+
+OverloadedRE = /([^_]*)(?:_(.*))/
+
+MANAGER_INCLUDES = [
+ "gen_cpp",
+ "/usr/local/include/thrift/",
+ "/home/ekarak/ozw/open-zwave-read-only/cpp/tinyxml",
+ "/home/ekarak/ozw/open-zwave-read-only/cpp/src",
+ "/home/ekarak/ozw/open-zwave-read-only/cpp/src/value_classes",
+ "/home/ekarak/ozw/open-zwave-read-only/cpp/src/command_classes",
+ "/home/ekarak/ozw/open-zwave-read-only/cpp/src/platform",
+]
+
+#
+# must load all source files in a single batch (RbGCCXML gets confused otherwise...)
+#
+files = [
+ "/home/ekarak/ozw/thrift4ozw/gen-cpp/RemoteManager_server.skeleton.cpp",
+ "/home/ekarak/ozw/open-zwave-read-only/cpp/src/Manager.cpp"
+]
+puts "Parsing:" + files.join("\n\t")
+RootNode = RbGCCXML.parse(files, :includes => MANAGER_INCLUDES)
+
+# read skeleton file in memory as an array
+output = File.open("gen-cpp/RemoteManager_server.skeleton.cpp").readlines
+
+def ValueID_converter(arg)
+ return "*g_values[#{arg}]"
+end
+
+# fix the constructor
+lineno = RootNode.classes("RemoteManagerHandler").constructors[1]['line'].to_i
+#~ output[lineno] = Constructor
+# add our extra hidden sauce
+#lineno = foo.classes("RemoteManagerHandler").constructors[1]['endline'].to_i
+#output[lineno] = Converter
+
+RootNode.classes("RemoteManagerHandler").methods.each { |meth|
+ # find line number, insert critical section enter code
+ lineno = meth['line'].to_i
+ #puts "Method #{meth.name} at line #{lineno}-------------------------"
+ output[lineno] = "\tManager* mgr = Manager::Get();\n\tg_criticalSection.lock();\n"
+ #
+ target_method = nil
+ target_method_name = nil
+ disambiguation_hint = nil
+
+ # skeleton function's name has underscore => Overloaded. Needs disambiguation.
+ if md = OverloadedRE.match(meth.name) then
+ target_method_name = md[1]
+ disambiguation_hint = md[2]
+ else
+ target_method_name = meth.name
+ end
+
+ #
+ # SEARCH FOR MATCHING FUNCTION IN OPENZWAVE::MANAGER
+ #
+ search_result = RootNode.namespaces("OpenZWave").classes("Manager").methods.find(:name => target_method_name, :access => :public)
+ #puts "search result: #{search_result.class.name}"
+ case search_result
+ when RbGCCXML::QueryResult then
+ raise "#{target_method_name}(): no disambiguation hint given!!!" unless disambiguation_hint
+ #puts " ...Overloaded method: #{meth.name}"
+ search_result.each { |node|
+ # last argument's type must match disambiguation_hint
+ target_method = node if node.arguments[-1].cpp_type.to_cpp =~ Regexp.new(disambiguation_hint, Regexp::IGNORECASE)
+ # FIXME:: ListString => list<string>
+ }
+ when RbGCCXML::Method then
+ #puts " ...exact match for #{meth.name}"
+ target_method = search_result
+ end
+
+ raise "Unable to resolve target method! (#{meth.name})" unless target_method
+
+ #
+ # TIME TO BOOGEY
+ #
+
+ puts "CREATING MAPPING for (#{meth.return_type.to_cpp}) #{meth.name}"
+
+ #Thrift transforms methods with complex return types (string, vector<...>, user-defined structs etc)
+ # example 1:
+ # (C++) string GetLibraryVersion( uint32 const _homeId );
+ # (thrift) string GetLibraryVersion( 1:i32 _homeId );
+ # (skeleton) void GetLibraryVersion(std::string& _return, const int32_t _homeId)
+ #
+ # example 2:
+ # (C++) uint32 GetNodeNeighbors( uint32 const _homeId, uint8 const _nodeId, uint8** _nodeNeighbors );
+ # (thrift) UInt32_NeighborMap GetNodeNeighbors( 1:i32 _homeId, 2:byte _nodeId);
+ # (skeleton) void GetNodeNeighbors(UInt32_ListByte& _return, const int32_t _homeId, const int8_t _nodeId)
+ # ozw_types.h: class UInt32_ListByte {
+ # int32_t retval;
+ # std::vector<int8_t> arg; *** notice manual copying needed from C-style pointer to pointers of uint8's (not very C++ish)
+ # }
+ #
+ # example 3:
+ # (C++) bool GetValueListItems( ValueID const& _id, vector<string>* o_value );
+ # (thrift) Bool_ListString GetValueListItems( 1:RemoteValueID _id );
+ # (skeleton) void GetValueListItems(Bool_ListString& _return, const RemoteValueID _id)
+ # ozw_types.h:class Bool_ListString {
+ # bool retval;
+ # std::vector<std::string> arg;
+ # }
+ #
+
+ # is the skeleton function's return type composite?
+ composite_return = false
+ function_return_clause = ''
+ returnarg = nil
+
+ # gather all required arguments
+ # key = target_method_argument (RbGCCXML::Node)
+ # value => source argument cpp definition (string) - e.g. "(cast) argname"
+
+ #arr = target_method.arguments.map {|tma| meth.arguments.select { |a| a.name == tma.name}[0]}
+
+ args = {} # target node => source node
+ target_method.arguments.each {|tma| args[tma] = meth.arguments.select { |a| a.name == tma.name}[0]}
+
+ #
+ # create the function call
+ #
+ arg_array = []
+ #
+ args.each { |tgt_arg, src_arg|
+ puts "tgt=#{tgt_arg.to_cpp}, src=#{src_arg and src_arg.qualified_name}"
+ ampersand = (tgt_arg.cpp_type.to_cpp.include?('*') ? '&' : '')
+ if src_arg then # argument names matched
+ # maybe it's an OpenZWave::ValueID ???
+ if (tgt_arg.to_cpp =~ /ValueID/) then
+ arg_array << ValueID_converter(src_arg.name)
+ else
+ arg_array << "(#{tgt_arg.cpp_type.to_cpp}) #{ampersand}#{src_arg.name}"
+ end
+ else # source argument not found by name, search elsewhere
+ puts "method #{meth.name}, argument #{tgt_arg.name} not found by name..."
+ # 1) try searching through thrift's special '_return' argument (if there is one)
+ if (returnarg = meth.arguments.select{ |a| a.name == "_return"}[0]).is_a?(RbGCCXML::Argument) then
+ puts "Thrift special _return argument detected!"
+ last_argument_type = target_method.arguments[-1].cpp_type.to_cpp
+ if md = OverloadedRE.match(returnarg.cpp_type.to_cpp) then
+ # ...and it's a complex type (Thrift struct)
+ # 1st match is the function's return type
+ composite_return = true
+ function_return_clause = " _return.retval = "
+ # 2nd match is function's last argument type
+ arg_array << "(#{last_argument_type}) #{ampersand}_return#{composite_return ? '.arg' : ''}"
+ else
+ function_return_clause = " _return = "
+ # _return is a simple data type
+ arg_array << "(#{last_argument_type}) #{ampersand}_return"
+ end
+ else
+ puts "WARNING:: estimated argument '#{tgt_arg.name}' in method '#{meth.name}'!!!"
+ arg_array << "(#{tgt_arg.cpp_type.to_cpp}) #{ampersand}_return.arg"
+ end
+ end
+ }
+
+ # unleash the beast
+ fcall = "#{function_return_clause} mgr->#{target_method.name}(#{arg_array.join(', ')})"
+
+ #
+ # FUNCTION RETURN CLAUSE
+ #
+ case meth.return_type.name
+ when "void"
+ output[lineno+1] = "\t#{fcall};\n"
+ else
+ output[lineno+1] = "\t#{meth.return_type.to_cpp} function_result = #{fcall};\n"
+ end
+
+ # unlock the critical section
+ output[lineno+1] << "\tg_criticalSection.unlock();\n"
+ # output return statement (unless rettype == void)
+ unless meth.return_type.name == "void"
+ output[lineno+1] << "\treturn(function_result);\n"
+ end
+
+}
+
+output[0] = "// Automatically generated OpenZWave::Manager_server wrapper\n"
+output[1] = "// (c) 2011 Elias Karakoulakis <elias.karakoulakis@gmail.com>\n"
+# write out the generated file
+File.new("gen-cpp/RemoteManager_server.cpp", File::CREAT|File::TRUNC|File::RDWR, 0644) << output.join
View
7 ekarak-notes.txt
@@ -0,0 +1,7 @@
+thrift -gen cpp ozw.thrift
+ruby1.9.1 create_server.rb
+gcc -c gen-cpp/RemoteManager_server.cpp -I/usr/local/include/thrift/ -I/home/ekarak/ozw/open-zwave-read-only/cpp/src -I/home/ekarak/ozw/open-zwave-read-only/cpp/src/value_classes
+gcc -c gen-cpp/RemoteManager_server.cpp -I/usr/local/include/thrift/ -I/home/ekarak/ozw/open-zwave-read-only/cpp/src -I/home/ekarak/ozw/open-zwave-read-only/cpp/src/value_classes -lthrift /home/ekarak/ozw/open-zwave-read-only/cpp/lib/linux/openzwave.a -ludev
+
+gcc -c RemoteManager.cpp -I/usr/local/include/thrift/ -I/home/ekarak/ozw/open-zwave-read-only/cpp/src -I/home/ekarak/ozw/open-zwave-read-only/cpp/src/value_classes
+gcc RemoteManager_server.cpp -I/usr/local/include/thrift/ -I/home/ekarak/ozw/open-zwave-read-only/cpp/src -I/home/ekarak/ozw/open-zwave-read-only/cpp/src/value_classes -lthrift /home/ekarak/ozw/open-zwave-read-only/cpp/lib/linux/openzwave.a -ludev ozw_*.o RemoteManager.o
View
1,633 ozw.thrift
@@ -0,0 +1,1633 @@
+// OpenZWave Thrift interface
+// (c) 2011 Elias Karakoulakis
+
+namespace * OpenZWave
+cpp_include "Manager.h"
+cpp_include "ValueID.h"
+cpp_include "Options.h"
+
+enum RemoteValueGenre {
+ ValueGenre_Basic=0,
+ ValueGenre_User=1,
+ ValueGenre_Config=2,
+ ValueGenre_System=3,
+ ValueGenre_Count=4
+}
+
+enum RemoteValueType {
+ ValueType_Bool=0,
+ ValueType_Byte=1,
+ ValueType_Decimal=2,
+ ValueType_Int=3,
+ ValueType_List=4,
+ ValueType_Schedule=5,
+ ValueType_Short=6,
+ ValueType_String=7,
+ ValueType_Button=8,
+ ValueType_Max=8
+}
+
+//~ struct RemoteValueID {
+ //~ 1:i32 _homeId,
+ //~ 2:byte _nodeId,
+ //~ 3:RemoteValueGenre _genre,
+ //~ 4:byte _commandClassId,
+ //~ 5:byte _instance,
+ //~ 6:byte _valueIndex,
+ //~ 7:RemoteValueType _type
+//~ }
+typedef i64 RemoteValueID
+
+struct GetSwitchPointReturnStruct {
+ 1:byte o_hours;
+ 2:byte o_minutes;
+ 3:byte o_setback;
+}
+
+struct Bool_Bool {
+ 1:bool retval; // function succeeded?
+ 2:bool arg; // value returned
+}
+
+struct Bool_UInt8 {
+ 1:bool retval; // function succeeded?
+ 2:byte arg; // value returned
+}
+
+struct Bool_Float {
+ 1:bool retval; // function succeeded?
+ 2:double arg; // value returned
+}
+
+struct Bool_Int {
+ 1:bool retval; // function succeeded?
+ 2:i32 arg; // value returned
+}
+
+struct Bool_Int16 {
+ 1:bool retval; // function succeeded?
+ 2:i16 arg; // value returned
+}
+
+struct Bool_String {
+ 1:bool retval; // function succeeded?
+ 2:string arg; // value returned
+}
+
+struct Bool_ListString {
+ 1:bool retval;
+ 2:list<string> arg;
+}
+
+struct UInt32_ListByte {
+ 1:i32 retval;
+ 2:list<byte> arg;
+}
+
+/*-------------------------------------*/
+service RemoteManager {
+/*-------------------------------------*/
+
+ //-----------------------------------------------------------------------------
+ // Configuration
+ //-----------------------------------------------------------------------------
+ /** \name Configuration
+ * For saving the Z-Wave network configuration so that the entire network does not need to be
+ * polled every time the application starts.
+ */
+
+ // void WriteConfig( uint32 const _homeId );
+ void WriteConfig(1: i32 _homeId );
+
+ //TODO: Options* GetOptions()const{ return m_options; }
+ // list<Options> GetOptions();
+
+ //-----------------------------------------------------------------------------
+ // Drivers
+ //-----------------------------------------------------------------------------
+ /** \name Drivers
+ * Methods for adding and removing drivers and obtaining basic controller information.
+ */
+
+ //TODO: bool AddDriver( string const& _controllerPath, Driver::ControllerInterface const& _interface = Driver::ControllerInterface_Serial);
+ //TODO: bool RemoveDriver( string const& _controllerPath );
+
+ //uint8 GetControllerNodeId( uint32 const _homeId );
+ byte GetControllerNodeId( 1:i32 _homeId );
+
+ //bool IsPrimaryController( uint32 const _homeId );
+ bool IsPrimaryController(1: i32 _homeId );
+
+ //TODO
+ //bool IsStaticUpdateController( uint32 const _homeId );
+
+ /**
+ * \brief Query if the controller is using the bridge controller library.
+ * A bridge controller is able to create virtual nodes that can be associated
+ * with other controllers to enable events to be passed on.
+ * \param _homeId The Home ID of the Z-Wave controller.
+ * \return true if it is a bridge controller, false if not.
+ */
+ //bool IsBridgeController( uint32 const _homeId );
+ bool IsBridgeController( 1:i32 _homeId );
+
+ /**
+ * \brief Get the version of the Z-Wave API library used by a controller.
+ * \param _homeId The Home ID of the Z-Wave controller.
+ * \return a string containing the library version. For example, "Z-Wave 2.48".
+ */
+ //string GetLibraryVersion( uint32 const _homeId );
+ string GetLibraryVersion( 1:i32 _homeId );
+
+ /**
+ * \brief Get a string containing the Z-Wave API library type used by a controller.
+ * The possible library types are:
+ * - Static Controller
+ * - Controller
+ * - Enhanced Slave
+ * - Slave
+ * - Installer
+ * - Routing Slave
+ * - Bridge Controller
+ * - Device Under Test
+ * The controller should never return a slave library type.
+ * For a more efficient test of whether a controller is a Bridge Controller, use
+ * the IsBridgeController method.
+ * \param _homeId The Home ID of the Z-Wave controller.
+ * \return a string containing the library type.
+ * \see GetLibraryVersion, IsBridgeController
+ */
+ //string GetLibraryTypeName( uint32 const _homeId );
+ string GetLibraryTypeName( 1:i32 _homeId );
+
+ /**
+ * \brief Get count of messages in the outgoing send queue.
+ * \param _homeId The Home ID of the Z-Wave controller.
+ * \return a integer message count
+ */
+ //int32 GetSendQueueCount( uint32 const _homeId );
+ i32 GetSendQueueCount( 1:i32 _homeId );
+
+ //-----------------------------------------------------------------------------
+ // Polling Z-Wave devices
+ //-----------------------------------------------------------------------------
+ /** \name Polling Z-Wave devices
+ * Methods for controlling the polling of Z-Wave devices. Modern devices will not
+ * require polling. Some old devices need to be polled as the only way to detect
+ * status changes.
+ */
+
+ /**
+ * \brief Get the time period between polls of a node's state.
+ */
+ //int32 GetPollInterval();
+ i32 GetPollInterval();
+
+ /**
+ * \brief Set the time period between polls of a node's state.
+ * Due to patent concerns, some devices do not report state changes automatically to the controller.
+ * These devices need to have their state polled at regular intervals. The length of the interval
+ * is the same for all devices. To even out the Z-Wave network traffic generated by polling, OpenZWave
+ * divides the polling interval by the number of devices that have polling enabled, and polls each
+ * in turn. It is recommended that if possible, the interval should not be set shorter than the
+ * number of polled devices in seconds (so that the network does not have to cope with more than one
+ * poll per second).
+ * \param _seconds The length of the polling interval in seconds.
+ */
+ //void SetPollInterval( int32 _seconds );
+ void SetPollInterval( 1:i32 _seconds );
+
+ /**
+ * \brief Enable the polling of a device's state.
+ * \param _valueId The ID of the value to start polling.
+ * \return True if polling was enabled.
+ */
+ //bool EnablePoll( ValueID const _valueId );
+ bool EnablePoll( 1:RemoteValueID _valueId );
+
+ /**
+ * \brief Disable the polling of a device's state.
+ * \param _valueId The ID of the value to stop polling.
+ * \return True if polling was disabled.
+ */
+ //bool DisablePoll( ValueID const _valueId );
+ bool DisablePoll( 1:RemoteValueID _valueId );
+
+ /**
+ * \brief Determine the polling of a device's state.
+ * \param _valueId The ID of the value to check polling.
+ * \return True if polling is active.
+ */
+ //bool isPolled( ValueID const _valueId );
+ bool isPolled( 1:RemoteValueID _valueId );
+
+ //-----------------------------------------------------------------------------
+ // Node information
+ //-----------------------------------------------------------------------------
+ /** \name Node information
+ * Methods for accessing information on indivdual nodes.
+ */
+/**
+ * \brief Trigger the fetching of fixed data about a node.
+ * Causes the node's data to be obtained from the Z-Wave network in the same way as if it had just been added.
+ * This method would normally be called automatically by OpenZWave, but if you know that a node has been
+ * changed, calling this method will force a refresh of all of the data held by the library. This can be especially
+ * useful for devices that were asleep when the application was first run. This is the
+ * same as the query state starting from the beginning.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return True if the request was sent successfully.
+ */
+ //bool RefreshNodeInfo( uint32 const _homeId, uint8 const _nodeId );
+ bool RefreshNodeInfo( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Trigger the fetching of dynamic value data for a node.
+ * Causes the node's values to be requested from the Z-Wave network. This is the
+ * same as the query state starting from the associations state.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return True if the request was sent successfully.
+ */
+ //bool RequestNodeState( uint32 const _homeId, uint8 const _nodeId );
+ bool RequestNodeState( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Trigger the fetching of just the dynamic value data for a node.
+ * Causes the node's values to be requested from the Z-Wave network. This is the
+ * same as the query state starting from the dynamic state.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return True if the request was sent successfully.
+ */
+ //bool RequestNodeDynamic( uint32 const _homeId, uint8 const _nodeId );
+ bool RequestNodeDynamic( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get whether the node is a listening device that does not go to sleep
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return True if it is a listening node.
+ */
+ //bool IsNodeListeningDevice( uint32 const _homeId, uint8 const _nodeId );
+ bool IsNodeListeningDevice( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get whether the node is a routing device that passes messages to other nodes
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return True if the node is a routing device
+ */
+ //bool IsNodeRoutingDevice( uint32 const _homeId, uint8 const _nodeId );
+ bool IsNodeRoutingDevice( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the maximum baud rate of a node's communications
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return the baud rate in bits per second.
+ */
+ //uint32 GetNodeMaxBaudRate( uint32 const _homeId, uint8 const _nodeId );
+ i32 GetNodeMaxBaudRate( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the version number of a node
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return the node's version number
+ */
+ //uint8 GetNodeVersion( uint32 const _homeId, uint8 const _nodeId );
+ byte GetNodeVersion( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the security byte for a node. Bit meanings are still to be determined.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return the node's security byte
+ */
+ //uint8 GetNodeSecurity( uint32 const _homeId, uint8 const _nodeId );
+ byte GetNodeSecurity( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the basic type of a node.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return the node's basic type.
+ */
+ //uint8 GetNodeBasic( uint32 const _homeId, uint8 const _nodeId );
+ byte GetNodeBasic( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the generic type of a node.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return the node's generic type.
+ */
+ //uint8 GetNodeGeneric( uint32 const _homeId, uint8 const _nodeId );
+ byte GetNodeGeneric( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the specific type of a node.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return the node's specific type.
+ */
+ //uint8 GetNodeSpecific( uint32 const _homeId, uint8 const _nodeId );
+ byte GetNodeSpecific( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get a human-readable label describing the node
+ * The label is taken from the Z-Wave specific, generic or basic type, depending on which of those values are specified by the node.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return A string containing the label text.
+ */
+ //string GetNodeType( uint32 const _homeId, uint8 const _nodeId );
+ string GetNodeType( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the bitmap of this node's neighbors
+ *
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \param _nodeNeighbors An array of 29 uint8s to hold the neighbor bitmap
+ */
+ //uint32 GetNodeNeighbors( uint32 const _homeId, uint8 const _nodeId, uint8** _nodeNeighbors );
+ UInt32_ListByte GetNodeNeighbors( 1:i32 _homeId, 2:byte _nodeId);
+
+ /**
+ * \brief Get the manufacturer name of a device
+ * The manufacturer name would normally be handled by the Manufacturer Specific commmand class,
+ * taking the manufacturer ID reported by the device and using it to look up the name from the
+ * manufacturer_specific.xml file in the OpenZWave config folder.
+ * However, there are some devices that do not support the command class, so to enable the user
+ * to manually set the name, it is stored with the node data and accessed via this method rather
+ * than being reported via a command class Value object.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return A string containing the node's manufacturer name.
+ * \see SetNodeManufacturerName, GetNodeProductName, SetNodeProductName
+ */
+ //string GetNodeManufacturerName( uint32 const _homeId, uint8 const _nodeId );
+ string GetNodeManufacturerName( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the product name of a device
+ * The product name would normally be handled by the Manufacturer Specific commmand class,
+ * taking the product Type and ID reported by the device and using it to look up the name from the
+ * manufacturer_specific.xml file in the OpenZWave config folder.
+ * However, there are some devices that do not support the command class, so to enable the user
+ * to manually set the name, it is stored with the node data and accessed via this method rather
+ * than being reported via a command class Value object.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return A string containing the node's product name.
+ * \see SetNodeProductName, GetNodeManufacturerName, SetNodeManufacturerName
+ */
+ //string GetNodeProductName( uint32 const _homeId, uint8 const _nodeId );
+ string GetNodeProductName( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the name of a node
+ * The node name is a user-editable label for the node that would normally be handled by the
+ * Node Naming commmand class, but many devices do not support it. So that a node can always
+ * be named, OpenZWave stores it with the node data, and provides access through this method
+ * and SetNodeName, rather than reporting it via a command class Value object.
+ * The maximum length of a node name is 16 characters.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return A string containing the node's name.
+ * \see SetNodeName, GetNodeLocation, SetNodeLocation
+ */
+ //string GetNodeName( uint32 const _homeId, uint8 const _nodeId );
+ string GetNodeName( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the location of a node
+ * The node location is a user-editable string that would normally be handled by the Node Naming
+ * commmand class, but many devices do not support it. So that a node can always report its
+ * location, OpenZWave stores it with the node data, and provides access through this method
+ * and SetNodeLocation, rather than reporting it via a command class Value object.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return A string containing the node's location.
+ * \see SetNodeLocation, GetNodeName, SetNodeName
+ */
+ //string GetNodeLocation( uint32 const _homeId, uint8 const _nodeId );
+ string GetNodeLocation( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the manufacturer ID of a device
+ * The manufacturer ID is a four digit hex code and would normally be handled by the Manufacturer
+ * Specific commmand class, but not all devices support it. Although the value reported by this
+ * method will be an empty string if the command class is not supported and cannot be set by the
+ * user, the manufacturer ID is still stored with the node data (rather than being reported via a
+ * command class Value object) to retain a consistent approach with the other manufacturer specific data.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return A string containing the node's manufacturer ID, or an empty string if the manufactuer
+ * specific command class is not supported by the device.
+ * \see GetNodeProductType, GetNodeProductId, GetNodeManufacturerName, GetNodeProductName
+ */
+ //string GetNodeManufacturerId( uint32 const _homeId, uint8 const _nodeId );
+ string GetNodeManufacturerId( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the product type of a device
+ * The product type is a four digit hex code and would normally be handled by the Manufacturer Specific
+ * commmand class, but not all devices support it. Although the value reported by this method will
+ * be an empty string if the command class is not supported and cannot be set by the user, the product
+ * type is still stored with the node data (rather than being reported via a command class Value object)
+ * to retain a consistent approach with the other manufacturer specific data.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return A string containing the node's product type, or an empty string if the manufactuer
+ * specific command class is not supported by the device.
+ * \see GetNodeManufacturerId, GetNodeProductId, GetNodeManufacturerName, GetNodeProductName
+ */
+ //string GetNodeProductType( uint32 const _homeId, uint8 const _nodeId );
+ string GetNodeProductType( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get the product ID of a device
+ * The product ID is a four digit hex code and would normally be handled by the Manufacturer Specific
+ * commmand class, but not all devices support it. Although the value reported by this method will
+ * be an empty string if the command class is not supported and cannot be set by the user, the product
+ * ID is still stored with the node data (rather than being reported via a command class Value object)
+ * to retain a consistent approach with the other manufacturer specific data.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return A string containing the node's product ID, or an empty string if the manufactuer
+ * specific command class is not supported by the device.
+ * \see GetNodeManufacturerId, GetNodeProductType, GetNodeManufacturerName, GetNodeProductName
+ */
+ //string GetNodeProductId( uint32 const _homeId, uint8 const _nodeId );
+ string GetNodeProductId( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Set the manufacturer name of a device
+ * The manufacturer name would normally be handled by the Manufacturer Specific commmand class,
+ * taking the manufacturer ID reported by the device and using it to look up the name from the
+ * manufacturer_specific.xml file in the OpenZWave config folder.
+ * However, there are some devices that do not support the command class, so to enable the user
+ * to manually set the name, it is stored with the node data and accessed via this method rather
+ * than being reported via a command class Value object.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \param _manufacturerName A string containing the node's manufacturer name.
+ * \see GetNodeManufacturerName, GetNodeProductName, SetNodeProductName
+ */
+ //void SetNodeManufacturerName( uint32 const _homeId, uint8 const _nodeId, string const& _manufacturerName );
+ void SetNodeManufacturerName( 1:i32 _homeId, 2:byte _nodeId, 3:string _manufacturerName );
+
+ /**
+ * \brief Set the product name of a device
+ * The product name would normally be handled by the Manufacturer Specific commmand class,
+ * taking the product Type and ID reported by the device and using it to look up the name from the
+ * manufacturer_specific.xml file in the OpenZWave config folder.
+ * However, there are some devices that do not support the command class, so to enable the user
+ * to manually set the name, it is stored with the node data and accessed via this method rather
+ * than being reported via a command class Value object.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \param _productName A string containing the node's product name.
+ * \see GetNodeProductName, GetNodeManufacturerName, SetNodeManufacturerName
+ */
+ //void SetNodeProductName( uint32 const _homeId, uint8 const _nodeId, string const& _productName );
+ void SetNodeProductName( 1:i32 _homeId, 2:byte _nodeId, 3:string _productName );
+
+ /**
+ * \brief Set the name of a node
+ * The node name is a user-editable label for the node that would normally be handled by the
+ * Node Naming commmand class, but many devices do not support it. So that a node can always
+ * be named, OpenZWave stores it with the node data, and provides access through this method
+ * and GetNodeName, rather than reporting it via a command class Value object.
+ * If the device does support the Node Naming command class, the new name will be sent to the node.
+ * The maximum length of a node name is 16 characters.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \param _nodeName A string containing the node's name.
+ * \see GetNodeName, GetNodeLocation, SetNodeLocation
+ */
+ //void SetNodeName( uint32 const _homeId, uint8 const _nodeId, string const& _nodeName );
+ void SetNodeName( 1:i32 _homeId, 2:byte _nodeId, 3:string _nodeName );
+
+ /**
+ * \brief Set the location of a node
+ * The node location is a user-editable string that would normally be handled by the Node Naming
+ * commmand class, but many devices do not support it. So that a node can always report its
+ * location, OpenZWave stores it with the node data, and provides access through this method
+ * and GetNodeLocation, rather than reporting it via a command class Value object.
+ * If the device does support the Node Naming command class, the new location will be sent to the node.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \param _location A string containing the node's location.
+ * \see GetNodeLocation, GetNodeName, SetNodeName
+ */
+ //void SetNodeLocation( uint32 const _homeId, uint8 const _nodeId, string const& _location );
+ void SetNodeLocation( 1:i32 _homeId, 2:byte _nodeId, 3:string _location );
+
+ /**
+ * \brief Turns a node on
+ * This is a helper method to simplify basic control of a node. It is the equivalent of
+ * changing the level reported by the node's Basic command class to 255, and will generate a
+ * ValueChanged notification from that class. This command will turn on the device at its
+ * last known level, if supported by the device, otherwise it will turn it on at 100%.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to be changed.
+ * \see SetNodeOff, SetNodeLevel
+ */
+ //void SetNodeOn( uint32 const _homeId, uint8 const _nodeId );
+ void SetNodeOn( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Turns a node off
+ * This is a helper method to simplify basic control of a node. It is the equivalent of
+ * changing the level reported by the node's Basic command class to zero, and will generate
+ * a ValueChanged notification from that class.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to be changed.
+ * \see SetNodeOn, SetNodeLevel
+ */
+ //void SetNodeOff( uint32 const _homeId, uint8 const _nodeId );
+ void SetNodeOff( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Sets the basic level of a node
+ * This is a helper method to simplify basic control of a node. It is the equivalent of
+ * changing the value reported by the node's Basic command class and will generate a
+ * ValueChanged notification from that class.
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to be changed.
+ * \param _level The level to set the node. Valid values are 0-99 and 255. Zero is off and
+ * 99 is fully on. 255 will turn on the device at its last known level (if supported).
+ * \see SetNodeOn, SetNodeOff
+ */
+ //void SetNodeLevel( uint32 const _homeId, uint8 const _nodeId, uint8 const _level );
+ void SetNodeLevel( 1:i32 _homeId, 2:byte _nodeId, 3:byte _level );
+
+ /**
+ * \brief Get whether the node information has been received
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \return True if the node information has been received yet
+ */
+ //bool IsNodeInfoReceived( uint32 const _homeId, uint8 const _nodeId );
+ bool IsNodeInfoReceived( 1:i32 _homeId, 2:byte _nodeId );
+
+ /**
+ * \brief Get whether the node has the defined class available or not
+ * \param _homeId The Home ID of the Z-Wave controller that manages the node.
+ * \param _nodeId The ID of the node to query.
+ * \param _commandClassId Id of the class to test for
+ * \return True if the node does have the class instantiated, will return name & version
+ */
+ //TODO: define complex return structure
+ //bool GetNodeClassInformation( uint32 const _homeId, uint8 const _nodeId, uint8 const _commandClassId, string *_className = NULL, uint8 *_classVersion = NULL);
+
+
+ //-----------------------------------------------------------------------------
+ // Values
+ //-----------------------------------------------------------------------------
+ /** \name Values
+ * Methods for accessing device values. All the methods require a ValueID, which will have been provided
+ * in the ValueAdded Notification callback when the the value was first discovered by OpenZWave.
+ */
+ /**
+ * \brief Gets the user-friendly label for the value.
+ * \param _id The unique identifier of the value.
+ * \return The value label.
+ * \see ValueID
+ */
+ //string GetValueLabel( ValueID const& _id );
+ string GetValueLabel( 1:RemoteValueID _id );
+
+ /**
+ * \brief Sets the user-friendly label for the value.
+ * \param _id The unique identifier of the value.
+ * \param _value The new value of the label.
+ * \see ValueID
+ */
+ //void SetValueLabel( ValueID const& _id, string const& _value );
+ void SetValueLabel( 1:RemoteValueID _id, 2:string _value );
+
+ /**
+ * \brief Gets the units that the value is measured in.
+ * \param _id The unique identifier of the value.
+ * \return The value units.
+ * \see ValueID
+ */
+ //string GetValueUnits( ValueID const& _id );
+ string GetValueUnits( 1:RemoteValueID _id );
+
+ /**
+ * \brief Sets the units that the value is measured in.
+ * \param _id The unique identifier of the value.
+ * \param _value The new value of the units.
+ * \see ValueID
+ */
+ //void SetValueUnits( ValueID const& _id, string const& _value );
+ void SetValueUnits( 1:RemoteValueID _id, 2:string _value );
+
+ /**
+ * \brief Gets a help string describing the value's purpose and usage.
+ * \param _id The unique identifier of the value.
+ * \return The value help text.
+ * \see ValueID
+ */
+ //string GetValueHelp( ValueID const& _id );
+ string GetValueHelp( 1:RemoteValueID _id );
+
+ /**
+ * \brief Sets a help string describing the value's purpose and usage.
+ * \param _id The unique identifier of the value.
+ * \param _value The new value of the help text.
+ * \see ValueID
+ */
+ //void SetValueHelp( ValueID const& _id, string const& _value );
+ void SetValueHelp( 1:RemoteValueID _id, 2:string _value );
+
+ /**
+ * \brief Gets the minimum that this value may contain.
+ * \param _id The unique identifier of the value.
+ * \return The value minimum.