Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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