Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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