Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 426 lines (373 sloc) 15.653 kb
be7923d Added LGPL licence headers
Elias Karakoulakis authored
1 /*
2 Thrift4OZW - An Apache Thrift wrapper for OpenZWave
3 ----------------------------------------------------
4 Copyright (c) 2011 Elias Karakoulakis <elias.karakoulakis@gmail.com>
5
6 SOFTWARE NOTICE AND LICENSE
7
8 Thrift4OZW is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation, either version 3 of the License,
11 or (at your option) any later version.
12
13 Thrift4OZW is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with Thrift4OZW. If not, see <http://www.gnu.org/licenses/>.
20
21 for more information on the LGPL, see:
22 http://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License
23 */
24
8603286 Initial repo set-up
Elias Karakoulakis authored
25 //
be7923d Added LGPL licence headers
Elias Karakoulakis authored
26 // Main.cpp: OpenZWave Thrift Server for Project Ansible
8603286 Initial repo set-up
Elias Karakoulakis authored
27 // (c) 2011 Elias Karakoulakis <elias.karakoulakis@gmail.com>
28 //
29
30 #include "RemoteManager.h"
31 #include <protocol/TBinaryProtocol.h>
32 #include <server/TSimpleServer.h>
33 #include <transport/TServerSocket.h>
34 #include <transport/TBufferTransports.h>
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
35 #include <string>
36 #include <sstream>
37 #include <iostream>
8bb9da5 use boost::program_options to parse arguments to main()
Elias Karakoulakis authored
38 // we're using Boost's program_options
39 #include <boost/program_options.hpp>
40 namespace po = boost::program_options;
8603286 Initial repo set-up
Elias Karakoulakis authored
41
42 using namespace ::apache::thrift;
43 using namespace ::apache::thrift::protocol;
44 using namespace ::apache::thrift::transport;
45 using namespace ::apache::thrift::server;
46
47 using boost::shared_ptr;
48
49 using namespace OpenZWave;
50
51 // boost: extra includes
52 #include <boost/thread.hpp>
53 // the global mutex for accessing OpenZWave library
54 static boost::recursive_mutex g_criticalSection;
55
56 //
57 // Struct to hold all valid OpenZWave ValueID's
58 // (used by RemoteManager_server.cpp)
59 typedef struct
60 {
61 uint32 m_homeId;
62 uint8 m_nodeId;
63 bool m_polled;
67efa38 add SendAllValues() to send all known ValueID's to STOMP
Elias Karakoulakis authored
64 //std::map<uint64, ValueID*> m_values;
65 list<ValueID> m_values;
8603286 Initial repo set-up
Elias Karakoulakis authored
66 } NodeInfo;
67 //
be7923d Added LGPL licence headers
Elias Karakoulakis authored
68 static list<NodeInfo*> g_nodes;
34c2ebe More OpenZWave compatibility fixes
Elias Karakoulakis authored
69
8603286 Initial repo set-up
Elias Karakoulakis authored
70 // OpenZWave includes
71 #include "Notification.h"
72
73 // PocoStromp
74 #include "PocoStomp.h"
75
76 static uint32 g_homeId = 0;
77 static bool g_initFailed = false;
78 //
79
80 static boost::condition_variable initCond ;
81 static boost::mutex initMutex;
82
83 // STOMP statics
84 static STOMP::PocoStomp* stomp_client;
85 static string* notifications_topic = new string("/queue/zwave/monitor");
86
87 //-----------------------------------------------------------------------------
88 // <GetNodeInfo>
89 // Callback that is triggered when a value, group or node changes
90 //-----------------------------------------------------------------------------
91 NodeInfo* GetNodeInfo
92 (
93 Notification const* _notification
94 )
95 {
96 uint32 const homeId = _notification->GetHomeId();
97 uint8 const nodeId = _notification->GetNodeId();
98 for( list<NodeInfo*>::iterator it = g_nodes.begin(); it != g_nodes.end(); ++it )
99 {
100 NodeInfo* nodeInfo = *it;
101 if( ( nodeInfo->m_homeId == homeId ) && ( nodeInfo->m_nodeId == nodeId ) )
102 {
103 return nodeInfo;
104 }
105 }
106
107 return NULL;
108 }
109
110 //-----------------------------------------------------------------------------
111 // <OnNotification>
112 // Callback that is triggered by OpenZWave when a value, group or node changes
113 //-----------------------------------------------------------------------------
114 void OnNotification
115 (
116 Notification const* _notification,
117 void* _context
118 )
119 {
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
120 bool notify_stomp = true;
be7923d Added LGPL licence headers
Elias Karakoulakis authored
121 bool send_valueID = false;
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
122
8603286 Initial repo set-up
Elias Karakoulakis authored
123 // Must do this inside a critical section to avoid conflicts with the main thread
124 g_criticalSection.lock();
125
126 switch( _notification->GetType() )
127 {
128 /**< A new node value has been added to OpenZWave's list.
129 These notifications occur after a node has been discovered,
130 and details of its command classes have been received.
131 Each command class may generate one or more values,
132 depending on the complexity of the item being represented. */
133 case Notification::Type_ValueAdded:
134 {
135 if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
136 {
137 // Add the new value to the node's value list
138 ValueID v = _notification->GetValueID();
67efa38 add SendAllValues() to send all known ValueID's to STOMP
Elias Karakoulakis authored
139 nodeInfo->m_values.push_back( v );
8603286 Initial repo set-up
Elias Karakoulakis authored
140 }
be7923d Added LGPL licence headers
Elias Karakoulakis authored
141 send_valueID = true;
8603286 Initial repo set-up
Elias Karakoulakis authored
142 break;
143 }
144
145 /**< A node value has been removed from OpenZWave's list.
146 This only occurs when a node is removed. */
147 case Notification::Type_ValueRemoved:
148 {
67efa38 add SendAllValues() to send all known ValueID's to STOMP
Elias Karakoulakis authored
149 if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
150 {
8603286 Initial repo set-up
Elias Karakoulakis authored
151 // Remove the value from out list
67efa38 add SendAllValues() to send all known ValueID's to STOMP
Elias Karakoulakis authored
152 for( list<ValueID>::iterator it = nodeInfo->m_values.begin(); it != nodeInfo->m_values.end(); ++it )
153 {
154 if( (*it) == _notification->GetValueID() )
155 {
156 nodeInfo->m_values.erase( it );
157 break;
158 }
159 }
160
161 //~ // Remove the value from out list
162 for( list<ValueID>::iterator it = nodeInfo->m_values.begin(); it != nodeInfo->m_values.end(); ++it )
163 {
164 if( (*it) == _notification->GetValueID() )
165 {
166 nodeInfo->m_values.erase( it );
167 break;
168 }
169 }
170 }
be7923d Added LGPL licence headers
Elias Karakoulakis authored
171 send_valueID = true;
8603286 Initial repo set-up
Elias Karakoulakis authored
172 break;
173 }
174
175 /**< A node value has been updated from the Z-Wave network. */
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
176 case Notification::Type_ValueChanged: {
be7923d Added LGPL licence headers
Elias Karakoulakis authored
177 send_valueID = true;
8603286 Initial repo set-up
Elias Karakoulakis authored
178 /**< The associations for the node have changed. The application
179 should rebuild any group information it holds about the node. */
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
180 }
8603286 Initial repo set-up
Elias Karakoulakis authored
181 case Notification::Type_Group:
182 /**< A new node has been found (not already stored in zwcfg*.xml file) */
183 case Notification::Type_NodeNew:
184 /**< Basic node information has been receievd, such as whether the node is a
185 listening device, a routing device and its baud rate and basic, generic and
186 specific types. It is after this notification that you can call Manager::GetNodeType
187 to obtain a label containing the device description. */
188 case Notification::Type_NodeProtocolInfo:
189 /**< A node has triggered an event. This is commonly caused when a node sends
190 a Basic_Set command to the controller. The event value is stored in the notification. */
191 case Notification::Type_NodeEvent:
192 {
193 if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
194 {
195 // One of the node's association groups has changed
196 // TBD...
197 nodeInfo = nodeInfo;
198 }
199 break;
200 }
201
202
203 /**< A new node has been added to OpenZWave's list. This may be due
204 to a device being added to the Z-Wave network, or because
205 the application is initializing itself. */
206 case Notification::Type_NodeAdded:
207 {
208 // Add the new node to our list
209 NodeInfo* nodeInfo = new NodeInfo();
210 nodeInfo->m_homeId = _notification->GetHomeId();
211 nodeInfo->m_nodeId = _notification->GetNodeId();
212 nodeInfo->m_polled = false;
213 g_nodes.push_back( nodeInfo );
214 break;
215 }
216
217 /**< A node has been removed from OpenZWave's list. This may be due
218 to a device being removed from the Z-Wave network, or because the application is closing. */
219 case Notification::Type_NodeRemoved:
220 {
221 // Remove the node from our list
222 uint32 const homeId = _notification->GetHomeId();
223 uint8 const nodeId = _notification->GetNodeId();
224 for( list<NodeInfo*>::iterator it = g_nodes.begin(); it != g_nodes.end(); ++it )
225 {
226 NodeInfo* nodeInfo = *it;
227 if( ( nodeInfo->m_homeId == homeId ) && ( nodeInfo->m_nodeId == nodeId ) )
228 {
229 g_nodes.erase( it );
230 break;
231 }
232 }
233 break;
234 }
235
236 // Type_NodeNaming missing
237
238 /**< Polling of a node has been successfully turned off by a call to Manager::DisablePoll */
239 case Notification::Type_PollingDisabled:
240 {
241 if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
242 {
243 nodeInfo->m_polled = false;
244 }
245 break;
246 }
247
248 /**< Polling of a node has been successfully turned on by a call to Manager::EnablePoll */
249 case Notification::Type_PollingEnabled:
250 {
251 if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
252 {
253 nodeInfo->m_polled = true;
254 }
255 break;
256 }
257
258 /**< A driver for a PC Z-Wave controller has been added and is ready to use. The notification
259 will contain the controller's Home ID, which is needed to call most of the Manager methods. */
260 case Notification::Type_DriverReady:
261 {
262 g_homeId = _notification->GetHomeId();
263 break;
264 }
265
266 /**< Driver failed to load */
267 case Notification::Type_DriverFailed:
268 {
269 g_initFailed = true;
270 initCond.notify_all();
271 break;
272 }
273
274 //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. */
275 //missing MsgComplete < The last message that was sent is now complete. */
276 //missing Type_EssentialNodeQueriesComplete, /**< The queries on a node that are essential to its operation have been completed. The node can now handle incoming messages. */
277 //missing Type_NodeQueriesComplete, /**< All the initialisation queries on a node have been completed. */
278
279 /*< All awake nodes have been queried, so client application can expected complete data for these nodes. */
280 case Notification::Type_AwakeNodesQueried:
281
282 /**< All nodes have been queried, so client application can expected complete data. */
283 case Notification::Type_AllNodesQueried:
284 {
285 initCond.notify_all();
286 break;
287 }
288
289 default:
290 {
291 }
292 }
293
294 // now we can send the captured event to STOMP queue
295 //
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
296 if (notify_stomp) {
297 STOMP::hdrmap headers;
be7923d Added LGPL licence headers
Elias Karakoulakis authored
298 headers["NotificationNodeId"] = to_string<uint16_t>(_notification->GetNodeId(), std::hex);
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
299 headers["NotificationType"] = to_string<uint32_t>(_notification->GetType(), std::hex);
be7923d Added LGPL licence headers
Elias Karakoulakis authored
300 headers["NotificationByte"] = to_string<uint16_t>(_notification->GetByte(), std::hex);
301 if (send_valueID) {
302 headers["HomeID"] = to_string<uint32_t>(_notification->GetValueID().GetHomeId(), std::hex);
67efa38 add SendAllValues() to send all known ValueID's to STOMP
Elias Karakoulakis authored
303 headers["ValueID"] = to_string<uint64_t>(_notification->GetValueID().GetId(), std::hex);
be7923d Added LGPL licence headers
Elias Karakoulakis authored
304 }
4951e1d create_server.rb: refactored code
Elias Karakoulakis authored
305 //
306 string empty = "" ;
307 stomp_client->send(*notifications_topic, headers, empty);
308 }
8603286 Initial repo set-up
Elias Karakoulakis authored
309 //
310 g_criticalSection.unlock();
311 }
312
67efa38 add SendAllValues() to send all known ValueID's to STOMP
Elias Karakoulakis authored
313 // Send all known values via STOMP
314 void send_all_values() {
be7923d Added LGPL licence headers
Elias Karakoulakis authored
315 //
316 g_criticalSection.lock();
317 //
318 for( list<NodeInfo*>::iterator node_it = g_nodes.begin(); node_it != g_nodes.end(); ++node_it )
319 {
320 NodeInfo* nodeInfo = *node_it;
321
322 for( list<ValueID>::iterator val_iter = nodeInfo->m_values.begin(); val_iter != nodeInfo->m_values.end(); ++val_iter )
323 {
324 ValueID v = *val_iter;
325 STOMP::hdrmap headers;
326 headers["HomeID"] = to_string<uint32_t>(v.GetHomeId(), std::hex);
327 headers["ValueID"] = to_string<uint64_t>(v.GetId(), std::hex);
328 //
329 string empty = "" ;
330 stomp_client->send(*notifications_topic, headers, empty);
331 }
332 }
333 //
334 g_criticalSection.unlock();
67efa38 add SendAllValues() to send all known ValueID's to STOMP
Elias Karakoulakis authored
335 }
336
bde506c [Begin/Cancel]ControllerCommand implementation, callback skeleton mec…
Elias Karakoulakis authored
337 // the Thrift-generated (and manually patched) RemoteManager implementation
8603286 Initial repo set-up
Elias Karakoulakis authored
338 // for OpenZWave::Manager class
339 #include "gen-cpp/RemoteManager_server.cpp"
340 //
341
8bb9da5 use boost::program_options to parse arguments to main()
Elias Karakoulakis authored
342
343 // -----------------------------------------
344 int main(int argc, char *argv[]) {
345 // -----------------------------------------
346 string stomp_host = "localhost";
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
347 int stomp_port = 61613;
8bb9da5 use boost::program_options to parse arguments to main()
Elias Karakoulakis authored
348 string ozw_config_dir = "/home/ekarak/ozw/open-zwave-read-only/config/";
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
349 string ozw_user = "";
8bb9da5 use boost::program_options to parse arguments to main()
Elias Karakoulakis authored
350 string ozw_port = "/dev/ttyUSB0";
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
351 int thrift_port = 9090;
8bb9da5 use boost::program_options to parse arguments to main()
Elias Karakoulakis authored
352
353 try {
354 // Declare the supported options.
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
355 po::options_description desc("Project Ansible - OpenZWave orbiter");
8bb9da5 use boost::program_options to parse arguments to main()
Elias Karakoulakis authored
356 desc.add_options()
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
357 ("help,?", "print this help message")
358 ("stomphost,h", po::value(&stomp_host), "STOMP server hostname")
359 ("stompport,s", po::value(&stomp_port), "STOMP server port number")
360 ("thriftport,t", po::value(&thrift_port), "Thrift service port")
361 ("ozwconf,c", po::value(&ozw_config_dir), "OpenZWave config/ path (manufacturer database)")
362 ("ozwuser,u", po::value(&ozw_user), "OpenZWave user path (network & configuration state)")
363 ("ozwport,p", po::value(&ozw_port), "OpenZWave driver port (e.g. /dev/ttyUSB0)")
8bb9da5 use boost::program_options to parse arguments to main()
Elias Karakoulakis authored
364 ;
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
365 // a boost:program_options variable map
8bb9da5 use boost::program_options to parse arguments to main()
Elias Karakoulakis authored
366 po::variables_map vm;
367 po::store(po::parse_command_line(argc, argv, desc), vm);
368 po::notify(vm);
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
369 // exit on help
8bb9da5 use boost::program_options to parse arguments to main()
Elias Karakoulakis authored
370 if (vm.count("help")) {
371 cout << desc << "\n";
372 return 1;
373 }
374 }
375 catch (exception& e) {
376 cerr << "Error parsing options: " << e.what() << "\n";
377 return 2;
378 }
379
380 // ------------------
381 try {
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
382 // connect to STOMP server in order to send openzwave notifications
8bb9da5 use boost::program_options to parse arguments to main()
Elias Karakoulakis authored
383 stomp_client = new STOMP::PocoStomp(stomp_host, stomp_port);
384 stomp_client->connect();
385 } catch (exception& e) {
386 cerr << "Error connecting to STOMP: " << e.what() << "\n";
387 return 3;
388 }
8603286 Initial repo set-up
Elias Karakoulakis authored
389
390 // OpenZWave initialization
391 //initMutex.lock();
392
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
393 // Create the OpenZWave Manager.
8603286 Initial repo set-up
Elias Karakoulakis authored
394 // The first argument is the path to the config files (where the manufacturer_specific.xml file is located
395 // The second argument is the path for saved Z-Wave network state and the log file. If you leave it NULL
396 // the log file will appear in the program's working directory.
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
397 Options::Create(ozw_config_dir, ozw_user, "" );
8603286 Initial repo set-up
Elias Karakoulakis authored
398 Options::Get()->Lock();
399
400 Manager::Create();
401
8bb9da5 use boost::program_options to parse arguments to main()
Elias Karakoulakis authored
402 // Add a callback handler to the manager.
8603286 Initial repo set-up
Elias Karakoulakis authored
403 Manager::Get()->AddWatcher( OnNotification, NULL );
404
405 // Add a Z-Wave Driver
406 Manager::Get()->AddDriver( ozw_port );
407 //Manager::Get()->AddDriver( "HID Controller", Driver::ControllerInterface_Hid );
408
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
409 // THRIFT: initialize RemoteManager
8603286 Initial repo set-up
Elias Karakoulakis authored
410 shared_ptr<RemoteManagerHandler> handler(new RemoteManagerHandler());
411 shared_ptr<TProcessor> processor(new RemoteManagerProcessor(handler));
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
412 TServerSocket* ss = new TServerSocket(thrift_port);
be7923d Added LGPL licence headers
Elias Karakoulakis authored
413 ss->setRecvTimeout(3000);
414 shared_ptr<TServerTransport> serverTransport(ss);
8603286 Initial repo set-up
Elias Karakoulakis authored
415 shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
416 shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
417 TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
3e28ed8 further options in cmd-line flags.
Elias Karakoulakis authored
418
419 // Now we just wait for the driver to become ready, and then write out the loaded config.
420 boost::unique_lock<boost::mutex> initLock(initMutex);
421 initCond.wait(initLock);
422
423 // start up the Thrift RemoteManager server
8603286 Initial repo set-up
Elias Karakoulakis authored
424 server.serve();
425 return 0;
426 }
Something went wrong with that request. Please try again.