This URL has Read-Only access.

Statistics
| Branch: | Tag: | Revision:

root / src / spin / spinContext.cpp @ 8506491e

History | View | Annotate | Download (11.4 kB)

1
// -----------------------------------------------------------------------------
2
// |    ___  ___  _  _ _     ___                                        _      |
3
// |   / __>| . \| || \ |   | __>_ _  ___ ._ _ _  ___  _ _ _  ___  _ _ | |__   |
4
// |   \__ \|  _/| ||   |   | _>| '_><_> || ' ' |/ ._>| | | |/ . \| '_>| / /   |
5
// |   <___/|_|  |_||_\_|   |_| |_|  <___||_|_|_|\___.|__/_/ \___/|_|  |_\_\   |
6
// |                                                                           |
7
// |---------------------------------------------------------------------------|
8
//
9
// http://spinframework.sourceforge.net
10
// Copyright (C) 2009 Mike Wozniewski, Zack Settel
11
//
12
// Developed/Maintained by:
13
//    Mike Wozniewski (http://www.mikewoz.com)
14
//    Zack Settel (http://www.sheefa.net/zack)
15
//
16
// Principle Partners:
17
//    Shared Reality Lab, McGill University (http://www.cim.mcgill.ca/sre)
18
//    La Societe des Arts Technologiques (http://www.sat.qc.ca)
19
//
20
// Funding by:
21
//    NSERC/Canada Council for the Arts - New Media Initiative
22
//    Heritage Canada
23
//    Ministere du Developpement economique, de l'Innovation et de l'Exportation
24
//
25
// -----------------------------------------------------------------------------
26
//  This file is part of the SPIN Framework.
27
//
28
//  SPIN Framework is free software: you can redistribute it and/or modify
29
//  it under the terms of the GNU Lesser General Public License as published by
30
//  the Free Software Foundation, either version 3 of the License, or
31
//  (at your option) any later version.
32
//
33
//  SPIN Framework is distributed in the hope that it will be useful,
34
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
35
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36
//  GNU Lesser General Public License for more details.
37
//
38
//  You should have received a copy of the GNU Lesser General Public License
39
//  along with SPIN Framework. If not, see <http://www.gnu.org/licenses/>.
40
// -----------------------------------------------------------------------------
41

    
42
#include <string>
43
#include <iostream>
44
#include <pthread.h>
45

    
46
#include <osgDB/Registry>
47
#include <osgIntrospection/Type>
48

    
49
#include <lo/lo.h>
50
#include <lo/lo_lowlevel.h>
51

    
52
#include "spinUtil.h"
53
#include "spinContext.h"
54
#include "nodeVisitors.h"
55

    
56
using namespace std;
57

    
58

    
59
pthread_mutex_t pthreadLock = PTHREAD_MUTEX_INITIALIZER;
60

    
61

    
62

    
63
spinContext::spinContext(spinContextMode initMode)
64
{
65

    
66
#ifdef __Darwin
67
    setenv("OSG_LIBRARY_PATH", "@executable_path/../PlugIns", 1);
68
    //#define OSG_LIBRARY_PATH @executable_path/../PlugIns
69
#endif
70

    
71
        // Load the SPIN library:
72
        osgDB::Registry *reg = osgDB::Registry::instance();
73
        osgDB::DynamicLibrary::loadLibrary(reg->createLibraryNameForNodeKit("libSPIN"));
74

    
75

    
76
        // Make sure that our OSG nodekit is loaded (by checking for existance of
77
        // the ReferencedNode node type):
78
        const osgIntrospection::Type &ReferencedNodeType = osgIntrospection::Reflection::getType("ReferencedNode");
79
        if (!ReferencedNodeType.isDefined())
80
        {
81
                std::cout << "ERROR: libSPIN was not found. Please check dynamic libraries. Could not start spinContext." << std::endl;
82
                exit(1);
83
        }
84

    
85
        running = false;
86
        id = "default";
87

    
88
        if (!this->setMode(initMode))
89
        {
90
                std::cout << "ERROR: Unknown mode for spinContext" << std::endl;
91
                exit(1);
92
        }
93

    
94
        // default infoAddr:
95
        infoAddr = "224.0.0.1";
96
        infoPort = "54320";
97

    
98
        // override infoPort based on environment variable:
99
        char *infoPortStr = getenv("AS_INFOPORT");
100
        if (infoPortStr)
101
        {
102
            string tmpStr = string(infoPortStr);
103
                infoAddr = tmpStr.substr(0,tmpStr.rfind(":"));
104
                infoPort = tmpStr.substr(tmpStr.find(":")+1);
105
        }
106

    
107
        lo_infoAddr = lo_address_new(infoAddr.c_str(), infoPort.c_str());
108

    
109
        if (isMulticastAddress(infoAddr))
110
        {
111
                lo_infoServ = lo_server_thread_new_multicast(infoAddr.c_str(), infoPort.c_str(), oscParser_error);
112

    
113
        } else if (isBroadcastAddress(infoAddr))
114
        {
115
                lo_infoServ = lo_server_thread_new(infoPort.c_str(), oscParser_error);
116
                int sock = lo_server_get_socket_fd(lo_server_thread_get_server(lo_infoServ));
117
                int sockopt = 1;
118
                setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &sockopt, sizeof(sockopt));
119

    
120
        } else {
121
                lo_infoServ = lo_server_thread_new(infoPort.c_str(), oscParser_error);
122
        }
123

    
124
        // info channel callback (receives pings from client apps):
125
        lo_server_thread_add_method(lo_infoServ, NULL, NULL, infoChannelCallback, this);
126
                
127
        lo_server_thread_start(lo_infoServ);
128
        
129

    
130

    
131
        std::cout << "  INFO channel: " << lo_address_get_url(lo_infoAddr) << std::endl;
132
}
133

    
134
spinContext::~spinContext()
135
{
136
        this->stop();
137
        usleep(100);
138

    
139
        if (lo_infoServ)
140
        {
141
                lo_server_thread_stop(lo_infoServ);
142
                lo_server_thread_free(lo_infoServ);
143
        }
144

    
145
        if (lo_txAddr)
146
        {
147
                lo_address_free(lo_txAddr);
148
        }
149

    
150
        if (lo_infoAddr)
151
        {
152
                lo_address_free(lo_infoAddr);
153
        }
154
}
155

    
156

    
157
bool spinContext::setMode(spinContextMode m)
158
{
159
        if (running)
160
        {
161
                stop();
162
        }
163

    
164
        if (m==SERVER_MODE)
165
        {
166
                rxAddr = getMyIPaddress();
167
                rxPort = "54324";
168
                txAddr = "224.0.0.1";
169
                txPort = "54323";
170
                threadFunction = &spinServerThread;
171
        }
172
        
173
        else
174
        {
175
                rxAddr = "224.0.0.1";
176
                rxPort = "54323";
177
                txAddr = "224.0.0.1";
178
                txPort = "54324";
179
                threadFunction = &spinListenerThread;
180
        }
181

    
182
        this->mode = m;
183

    
184
        return true;
185
}
186

    
187
bool spinContext::start()
188
{
189
        lo_txAddr = lo_address_new(txAddr.c_str(), txPort.c_str());
190

    
191
        // create thread:
192
        if (pthread_attr_init(&pthreadAttr) < 0)
193
        {
194
                std::cout << "spinContext: could not prepare child thread" << std::endl;
195
                return false;
196
        }
197
        if (pthread_attr_setdetachstate(&pthreadAttr, PTHREAD_CREATE_DETACHED) < 0)
198
        {
199
                std::cout << "spinContext: could not prepare child thread" << std::endl;
200
                return false;
201
        }
202
        if (pthread_create( &pthreadID, &pthreadAttr, threadFunction, this) < 0)
203
        {
204
                std::cout << "spinContext: could not create new thread" << std::endl;
205
                return false;
206
        }
207

    
208
        //pthread_join(pthreadID, NULL); // if not DETACHED thread
209

    
210
        // wait until the thread gets into it's loop before returning:
211
        while (!running) usleep(10);
212

    
213
        return true;
214
}
215

    
216
void spinContext::stop()
217
{
218
        std::cout << "Stopping spinContext..." << std::endl;
219
        this->running = false;
220
}
221

    
222

    
223
void spinContext::sendInfoMessage(std::string OSCpath, lo_message msg)
224
{
225
        lo_send_message_from(lo_infoAddr, lo_server_thread_get_server(lo_infoServ), OSCpath.c_str(), msg);
226

    
227
    // Let's free the message after (not sure if this is necessary):
228
    lo_message_free(msg);
229
}
230

    
231
void spinContext::sendInfoMessage(std::string OSCpath, const char *types, ...)
232
{
233
        lo_message msg = lo_message_new();
234

    
235
        va_list ap;
236
        va_start(ap, types);
237

    
238
        int err = lo_message_add_varargs(msg, types, ap);
239

    
240
        if (!err)
241
        {
242
                sendInfoMessage(OSCpath, msg);
243
        } else {
244
                std::cout << "ERROR (spinContext::sendInfoMessage): " << err << std::endl;
245
        }
246
}
247

    
248
void spinContext::sendNodeMessage(t_symbol *nodeSym, lo_message msg)
249
{
250
        if (isRunning())
251
        {
252
                std::string OSCpath = "/SPIN/" + id + "/" + nodeSym->s_name;
253

    
254
                // If this thread is a listener, then we need to send an OSC message to
255
                // the rxAddr of the spinServer (unicast)
256
                if ( this->mode != SERVER_MODE )
257
                {
258
                        lo_send_message(lo_txAddr, OSCpath.c_str(), msg);
259
                }
260

    
261
                // if, however, this process acts as a server, we can optimize and send
262
                // directly to the OSC callback function:
263
                else SceneManagerCallback_node(OSCpath.c_str(), lo_message_get_types(msg), lo_message_get_argv(msg), lo_message_get_argc(msg), NULL, (void*)nodeSym);
264

    
265
        } else std::cout << "Error: tried to send message but SPIN is not running" << std::endl;
266

    
267
    // Let's free the message after (not sure if this is necessary):
268
    lo_message_free(msg);
269
}
270

    
271
void spinContext::sendNodeMessage(t_symbol *nodeSym, const char *types, ...)
272
{
273
        lo_message msg = lo_message_new();
274

    
275
        va_list ap;
276
        va_start(ap, types);
277

    
278
        int err = lo_message_add_varargs(msg, types, ap);
279

    
280
        if (!err)
281
        {
282
                sendNodeMessage(nodeSym, msg);
283
        } else {
284
                std::cout << "ERROR (spinContext::sendNodeMessage): " << err << std::endl;
285
        }
286

    
287
}
288

    
289

    
290
void spinContext::sendSceneMessage(lo_message msg)
291
{
292
        if (isRunning())
293
        {
294
                std::string OSCpath = "/SPIN/" + id;
295

    
296
                // If this thread is a listener, then we need to send an OSC message to
297
                // the rxAddr of the spinServer (unicast)
298
                if ( this->mode != SERVER_MODE )
299
                {
300
                        lo_send_message(lo_txAddr, OSCpath.c_str(), msg);
301
                }
302

    
303
                // if, however, this process acts as a server, we can optimize and send
304
                // directly to the OSC callback function:
305
                else SceneManagerCallback_admin(OSCpath.c_str(), lo_message_get_types(msg), lo_message_get_argv(msg), lo_message_get_argc(msg), NULL, (void*)sceneManager);
306

    
307
        } else std::cout << "Error: tried to send message but SPIN is not running" << std::endl;
308

    
309
    // Let's free the message after (not sure if this is necessary):
310
    lo_message_free(msg);
311
}
312

    
313
void spinContext::sendSceneMessage(const char *types, ...)
314
{
315
        lo_message msg = lo_message_new();
316

    
317
        va_list ap;
318
        va_start(ap, types);
319

    
320
        int err = lo_message_add_varargs(msg, types, ap);
321

    
322
        if (!err)
323
        {
324
                sendSceneMessage(msg);
325
        } else {
326
                std::cout << "ERROR (spinContext::sendSceneMessage): " << err << std::endl;
327
        }
328
}
329

    
330
// *****************************************************************************
331

    
332
static void *spinListenerThread(void *arg)
333
{
334
        spinContext *spin = (spinContext*) arg;
335

    
336
        std::cout << "  spinContext started in Listener mode" << std::endl;
337

    
338
        spin->sceneManager = new SceneManager(spin->id, spin->rxAddr, spin->rxPort);
339

    
340
        spin->running = true;
341
        while (spin->isRunning())
342
        {
343
                sleep(1);
344
                // do nothing (assume the app is doing updates - eg, in a draw loop)
345
        }
346

    
347
        // clean up:
348
        delete spin->sceneManager;
349

    
350
        pthread_exit(NULL);
351
}
352

    
353
static void *spinServerThread(void *arg)
354
{
355
        spinContext *spin = (spinContext*) arg;
356

    
357
        std::cout << "  spinContext started in Server mode" << std::endl;
358
        std::cout << "  broadcasting info messages on " << spin->txAddr << ", port: " << spin->infoPort << std::endl;
359

    
360
        spin->sceneManager = new SceneManager(spin->id, spin->rxAddr, spin->rxPort);
361
        spin->sceneManager->setTXaddress(spin->txAddr, spin->txPort);
362

    
363
        string myIP = getMyIPaddress();
364
        osg::Timer_t lastTick = osg::Timer::instance()->tick();
365
        osg::Timer_t frameTick = lastTick;
366

    
367
        // convert ports to integers for sending:
368
        int i_rxPort, i_txPort;
369
        fromString<int>(i_rxPort, spin->rxPort);
370
        fromString<int>(i_txPort, spin->txPort);
371

    
372
        UpdateSceneVisitor visitor;
373

    
374
        spin->running = true;
375
        while (spin->isRunning())
376
        {
377
                frameTick = osg::Timer::instance()->tick();
378
                if (osg::Timer::instance()->delta_s(lastTick,frameTick) > 5) // every 5 seconds
379
                {
380
                        spin->sendInfoMessage("/ping/SPIN", "ssisi", spin->id.c_str(), myIP.c_str(), i_rxPort, spin->txAddr.c_str(), i_txPort, LO_ARGS_END);
381
                        //lo_send_from(spin->lo_infoAddr, spin->lo_infoServ, LO_TT_IMMEDIATE, "/ping/SPIN", "ssisi", spin->id.c_str(), myIP.c_str(), i_rxPort, spin->txAddr.c_str(), i_txPort);
382
                        lastTick = frameTick;
383
                }
384

    
385
                pthread_mutex_lock(&pthreadLock);
386
                visitor.apply(*(spin->sceneManager->rootNode.get())); // only server should do this
387
                pthread_mutex_unlock(&pthreadLock);
388

    
389
                usleep(10);
390
        }
391

    
392
        // clean up:
393
        delete spin->sceneManager;
394

    
395
        pthread_exit(NULL);
396
}
397

    
398

    
399

    
400
int infoChannelCallback(const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data)
401
{
402
        spinContext *spin = (spinContext*) user_data;
403
        
404
        if (!spin) return 0;
405
        
406
        /*
407
        std::cout << "************ infoChannelCallback() got message: " << path << std::endl;
408
        
409
        printf("************ infoChannelCallback() got message: %s\n", (char*)path);
410
        printf("user_data: %s\n", (char*) user_data);
411
        for (int i=0; i<argc; i++) {
412
                printf("arg %d '%c' ", i, types[i]);
413
            lo_arg_pp((lo_type) types[i], argv[i]);
414
            printf("\n");
415
        }
416
        printf("\n");
417
        fflush(stdout);
418
         */
419
        
420
        if (spin->mode == spinContext::SERVER_MODE)
421
        {
422
                // TODO: monitor /ping/user messages, keep timeout handlers, 
423
                
424
        }
425
        
426
        
427
}