This URL has Read-Only access.

Statistics
| Branch: | Tag: | Revision:

root / src / spin / spinBaseContext.cpp @ aeb73a09

History | View | Annotate | Download (38.9 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

    
43
#include <string>
44
#include <iostream>
45
#include <pthread.h>
46
#include <signal.h>
47
#include <boost/lexical_cast.hpp>
48

    
49
#include <osgDB/Registry>
50
#include <cppintrospection/Type>
51
#include <cppintrospection/Value>
52
#include <osgUtil/Optimizer>
53
#include <osg/Version>
54

    
55
#include <boost/filesystem/operations.hpp>
56
#include <boost/filesystem/exception.hpp>
57
#ifndef DISABLE_PYTHON
58
#include <boost/python.hpp>
59
#endif
60

    
61
#include <lo/lo.h>
62
#include <lo/lo_lowlevel.h>
63

    
64
#include "SceneManager.h"
65
#include "spinBaseContext.h"
66
#include "spinServerContext.h"
67
#include "spinClientContext.h"
68
#include "spinUtil.h"
69
#include "spinApp.h"
70
#include "spinLog.h"
71
#include "nodeVisitors.h"
72
#include "SoundConnection.h"
73
#include "spinDefaults.h"
74
#include "config.h"
75

    
76
#ifdef WITH_SPATOSC
77
#include <spatosc/spatosc.h>
78
#endif
79

    
80

    
81
#define UNUSED(x) ( (void)(x) )
82

    
83
// TODO: make mutex a static member (perhaps of spinApp)?
84
pthread_mutex_t sceneMutex = PTHREAD_MUTEX_INITIALIZER;
85

    
86
namespace spin
87
{
88

    
89
/**
90
 * All threads need to stop according to the following flag. Note this used to
91
 * be a public class member, but we have sigHandler, which should be used
92
 * instead
93
 */
94
//namespace {
95
//    volatile bool signalStop;
96
//}
97
volatile bool spinBaseContext::signalStop = false;
98

    
99
spinBaseContext::spinBaseContext() :
100
    lo_infoAddr(NULL),
101
    lo_syncAddr(NULL),
102
    lo_infoServ_(NULL),
103
    lo_tcpRxServer_(NULL),
104
    pthreadID(0),
105
    doDiscovery_(true),
106
    autoPorts_(true),
107
    running(false)
108
{
109
    using namespace spin_defaults;
110
    
111
    signalStop = true;
112
    signal(SIGINT, sigHandler);
113

    
114
    // set default addresses (can be overridden):
115
    lo_infoAddr = lo_address_new(MULTICAST_GROUP, INFO_UDP_PORT);
116
    //lo_rxAddrs.push_back(lo_address_new(MULTICAST_GROUP, CLIENT_RX_UDP_PORT));
117
    //lo_txAddrs_.push_back(lo_address_new(MULTICAST_GROUP, CLIENT_TX_UDP_PORT));
118
    lo_syncAddr = lo_address_new(MULTICAST_GROUP, SYNC_UDP_PORT);
119
    
120

    
121
    tcpPort_ = SERVER_TCP_PORT;
122

    
123
    // override infoPort based on environment variable:
124
    char *infoPortStr = getenv("AS_INFOPORT");
125
    if (infoPortStr)
126
    {
127
        std::string tmpStr = std::string(infoPortStr);
128
        std::string infoAddr = tmpStr.substr(0, tmpStr.rfind(":"));
129
        std::string infoPort = tmpStr.substr(tmpStr.find(":") + 1);
130
        lo_address_free(lo_infoAddr);
131
        lo_infoAddr = lo_address_new(infoAddr.c_str(), infoPort.c_str());
132
    }
133
}
134

    
135
spinBaseContext::~spinBaseContext()
136
{
137
    this->stop();
138
        
139
    std::vector<lo_address>::iterator addrIter;
140
    for (addrIter = lo_txAddrs_.begin(); addrIter != lo_txAddrs_.end(); ++addrIter)
141
    {
142
            lo_address_free(*addrIter);
143
    }
144
    lo_txAddrs_.clear();
145
    
146
    for (addrIter = lo_rxAddrs_.begin(); addrIter != lo_rxAddrs_.end(); ++addrIter)
147
    {
148
            lo_address_free(*addrIter);
149
    }
150
    lo_rxAddrs_.clear();
151

    
152
    std::vector<lo_server>::iterator servIter;
153
    for (servIter = lo_rxServs_.begin(); servIter != lo_rxServs_.end(); ++servIter)
154
        lo_server_free(*servIter);
155
    lo_rxServs_.clear();
156

    
157
        lo_address_free(lo_infoAddr);
158
    lo_address_free(lo_syncAddr);
159

    
160
        // stop sceneManager OSC threads:
161
    //usleep(100);
162
        
163
    lo_server_free(lo_infoServ_);
164
    lo_server_free(lo_tcpRxServer_);
165
}
166

    
167
void spinBaseContext::debugPrint()
168
{
169
    std::cout << "\nSPIN context information:" << std::endl;
170
    std::cout << "  SceneManager ID:\t\t" << spinApp::Instance().getSceneID() << std::endl;
171
    std::cout << "  Resources path:\t\t" << spinApp::Instance().sceneManager->resourcesPath << std::endl;
172
    std::cout << "  SPIN version:\t\t\t" << PACKAGE_VERSION << "" << std::endl;
173
    std::cout << "  OSG version:\t\t\t" << osgGetVersion() << "" << std::endl;
174
#ifdef WITH_SPATOSC
175
    std::cout << "  SpatOSC version:\t\t"<< SPATOSC_VERSION << " (enabled=" << spinApp::Instance().hasAudioRenderer << ")" << std::endl;
176
#else
177
    std::cout << "  SpatOSC version:\t\tDISABLED" << std::endl;
178
#endif
179

    
180
#ifdef WITH_SHARED_VIDEO
181
    std::cout << "  sharedvideo enabled?\t\tYES" << std::endl;
182
#else
183
    std::cout << "  sharedvideo enabled?\t\tNO" << std::endl;
184
#endif
185
    std::cout << "  My IP address:\t\t" << getMyIPaddress() << std::endl;
186
    if (doDiscovery_)
187
    {
188
        std::cout << "  Auto discovery addr:\t\t" << lo_address_get_url(lo_infoAddr);
189
        if (lo_address_get_ttl(lo_infoAddr)>0)
190
            std::cout << " TTL=" << lo_address_get_ttl(lo_infoAddr);
191
        std::cout << std::endl;
192
    } else {
193
        std::cout << "  Auto discovery addr:\tOFF" << std::endl;
194
    }
195
    std::vector<lo_address>::iterator addrIter;
196
    for (addrIter = lo_txAddrs_.begin(); addrIter != lo_txAddrs_.end(); ++addrIter)
197
    {
198
        std::cout << "  Sending UDP to:\t\t" << lo_address_get_url(*addrIter);
199
        if (lo_address_get_ttl(*addrIter)>0)
200
            std::cout << " TTL=" << lo_address_get_ttl(*addrIter);
201
        std::cout << std::endl;
202
    }
203
    std::vector<lo_server>::iterator servIter;
204
    for (servIter = lo_rxServs_.begin(); servIter != lo_rxServs_.end(); ++servIter)
205
    {
206
        std::cout << "  Receiving UDP on:\t\t" << lo_server_get_url(*servIter) << std::endl;
207
    }
208
}
209

    
210
void spinBaseContext::addCommandLineOptions(osg::ArgumentParser *arguments)
211
{
212
    arguments->getApplicationUsage()->addCommandLineOption("-h or --help", "Display this information");
213
    arguments->getApplicationUsage()->addCommandLineOption("--version", "Display the version number and exit.");
214
    arguments->getApplicationUsage()->addCommandLineOption("--scene-id <id>", "Specify the id of the SPIN scene (Default: default)");
215
    arguments->getApplicationUsage()->addCommandLineOption("--disable-discovery", "Disables the multicast discovery service");
216
    arguments->getApplicationUsage()->addCommandLineOption("--spatosc <translator> <URL>", "Enables SPIN's internal SpatOSC scene. Example: --spatosc BasicTranslator osc.tcp://localhost:18033");
217

    
218
    // TODO: add discovery addr <host> <port> (defaults is MULTICAST_GROUP:INFO_UDP_PORT)
219
    // for a client, this would be --recv-udp-discovery <host> <port>
220
    // for a server, this would be --send-udp-discovery <host> <port>
221
}
222

    
223
int spinBaseContext::parseCommandLineOptions(osg::ArgumentParser *arguments)
224
{
225
    // if user request help or version write it out to cout and quit.
226
    if (arguments->read("-h") || arguments->read("--help"))
227
    {
228
        arguments->getApplicationUsage()->write(std::cout);
229
        return 0;
230
    }
231
    if (arguments->read("--version"))
232
    {
233
        std::cout << VERSION << std::endl;
234
        return 0;
235
    }
236

    
237
    std::string sceneID;
238
    if (arguments->read("--scene-id", sceneID))
239
    {
240
        spinApp::Instance().setSceneID(sceneID);
241
    }
242

    
243
    if (arguments->read("--disable-discovery"))
244
    {
245
        doDiscovery_ = false;
246
    }
247

    
248
    // set up SpatOSC:
249
    std::string translatorID, translatorType, translatorAddr, translatorPort;
250
    int translatorCount = 0;
251
    while (arguments->read("--spatosc", translatorType, translatorAddr))
252
    {
253
        #ifdef WITH_SPATOSC
254
        translatorID = "translator"+stringify(translatorCount++);
255
        spinApp::Instance().audioScene->addTranslator(translatorID, translatorType, translatorAddr);
256
        spinApp::Instance().hasAudioRenderer = true;
257
        #else
258
        std::cout << "WARNING: commandline option --spatosc not accepted. This version of SPIN was not built with support for SpatOSC." << std::endl;
259
        #endif
260
    }
261

    
262
    return 1;
263
}
264

    
265
void spinBaseContext::setLog(spinLog &log)
266
{
267
    std::vector<lo_server>::iterator it;
268
    for (it = lo_rxServs_.begin(); it != lo_rxServs_.end(); ++it)
269
    {
270
        lo_server_add_method((*it), NULL, NULL, logCallback, &log);
271
    }
272
}
273

    
274
void spinBaseContext::sigHandler(int signum)
275
{
276
    std::cout << "SPIN thread caught signal: " << signum << std::endl;
277

    
278
    spinBaseContext::signalStop = true;
279
    static int interruptCount = 0;
280
    const static int MAX_INTERRUPTS = 2;
281
    ++interruptCount;
282
    if (interruptCount >= MAX_INTERRUPTS)
283
        throw std::runtime_error("Got multiple interrupts, exitting rudely");
284
}
285

    
286
void spinBaseContext::setTTL(int ttl)
287
{
288
    std::vector<lo_address>::iterator addrIter;
289
    for (addrIter = lo_txAddrs_.begin(); addrIter != lo_txAddrs_.end(); ++addrIter)
290
        lo_address_set_ttl((*addrIter), ttl);
291

    
292
    lo_address_set_ttl(lo_infoAddr, ttl);
293
    lo_address_set_ttl(lo_syncAddr, ttl);
294
}
295
    
296
void spinBaseContext::addInfoHandler(EventHandler *obs)
297
{
298
    infoHandlers.push_back(obs);
299
}
300

    
301
void spinBaseContext::removeInfoHandler(EventHandler *obs)
302
{
303
    std::vector<EventHandler*>::iterator it;
304
    for (it=infoHandlers.begin(); it!=infoHandlers.end(); ++it)
305
    {
306
        if ((*it)==obs)
307
        {
308
            infoHandlers.erase(it);
309
            return;
310
        }
311
    }
312
}
313
void spinBaseContext::removeHandlerForAllEvents(EventHandler *obs)
314
{
315
    removeInfoHandler(obs);
316
    //removeNodeHandler(obs);
317
    //removeSceneHandler(obs);
318
}
319

    
320
/**
321
 * Startup point of the server's thread.
322
 */
323
bool spinBaseContext::startThread( void *(*threadFunction) (void*) )
324
{
325
    signalStop = false;
326

    
327
    // create thread:
328
    if (pthread_attr_init(&pthreadAttr) < 0)
329
    {
330
        std::cout << "spinBaseContext: could not prepare child thread" << std::endl;
331
        return false;
332
    }
333
    /*
334
       if (pthread_attr_setdetachstate(&pthreadAttr, PTHREAD_CREATE_DETACHED) < 0)
335
       {
336
       std::cout << "spinBaseContext: could not prepare child thread" << std::endl;
337
       return false;
338
       }
339
     */
340
    if (pthread_create( &pthreadID, &pthreadAttr, threadFunction, this) < 0)
341
    {
342
        std::cout << "spinBaseContext: could not create new thread" << std::endl;
343
        return false;
344
    }
345

    
346
    //pthread_join(pthreadID, NULL); // if not DETACHED thread
347

    
348
    // wait until the thread gets into it's loop before returning:
349
    while (! running )
350
        usleep(10);
351

    
352
    return true;
353
}
354

    
355
void spinBaseContext::stop()
356
{
357
    if (isRunning())
358
        signalStop = true;
359

    
360
    if (pthreadID != 0) 
361
    {
362
        pthread_join(pthreadID, NULL);
363
    }
364

    
365
    // wait here until the thread has really exited:
366
    while (isRunning())
367
        usleep(10);
368

    
369
}
370

    
371
int spinBaseContext::connectionCallback(const char *path, 
372
        const char *types, 
373
        lo_arg **argv, 
374
        int argc, 
375
        void * /*data*/, 
376
        void *user_data)
377
{
378
    std::string idStr;
379
    std::string theMethod;
380

    
381
    // make sure there is at least one argument (ie, a method to call):
382
    if (! argc)
383
        return 1;
384

    
385
    // get the method (argv[0]):
386
    if (lo_is_string_type((lo_type) types[0]))
387
    {
388
        theMethod = std::string((char *) argv[0]);
389
    }
390
    else
391
        return 1;
392

    
393
    // get the instance of the connection:
394
    SoundConnection *conn = (SoundConnection*) user_data;
395
    if (! conn)
396
    {
397
        std::cout << "oscParser: Could not find connection: " << idStr << std::endl;
398
        return 1;
399
    }
400

    
401
    // TODO: replace method call with osg::Introspection
402

    
403
    if (theMethod == "stateDump")
404
        conn->stateDump();
405
    else if (theMethod == "debug")
406
        conn->debug();
407
    else if ((argc==2) && (lo_is_numerical_type((lo_type) types[1])))
408
    {
409
        float value = lo_hires_val((lo_type) types[1], argv[1]);
410

    
411
        if (theMethod == "setThru")
412
            conn->setThru((bool) value);
413
        else if (theMethod == "setDistanceEffect")
414
            conn->setDistanceEffect(value);
415
        else if (theMethod == "setRolloffEffect")
416
            conn->setRolloffEffect(value);
417
        else if (theMethod == "setDopplerEffect")
418
            conn->setDopplerEffect(value);
419
        else if (theMethod == "setDiffractionEffect")
420
            conn->setDiffractionEffect(value);
421
        else
422
            std::cout << "Unknown OSC command: " << path << " " << theMethod << " (with " << argc-1 << " args)" << std::endl;
423
    }
424
    else
425
        std::cout << "Unknown OSC command: " << path << " " << theMethod << " (with " << argc-1 << " args)" << std::endl;
426
    return 1;
427
}
428

    
429
int spinBaseContext::nodeCallback(const char *path, const char *types, lo_arg **argv, int argc, void * /*data*/, void *user_data)
430
{
431
    // NOTE: user_data is a t_symbol pointer
432
    // FIXME: this method is way too long
433

    
434
    int i;
435
    std::string theMethod, nodeStr;
436
    cppintrospection::ValueList theArgs;
437

    
438
    // make sure there is at least one argument (ie, a method to call):
439
    if (! argc)
440
        return 1;
441
    // get the method (argv[0]):
442
    if (lo_is_string_type((lo_type)types[0]))
443
    {
444
        theMethod = std::string((char *)argv[0]);
445
    }
446
    else
447
        return 1;
448
    t_symbol *s = (t_symbol*) user_data;
449
    
450
    if (! s->s_thing)
451
    {
452
        std::cout << "oscParser: Could not find referenced object named: " << nodeStr << std::endl;
453
        return 1;
454
    }
455

    
456
    // get osgInrospection::Value from passed UserData by casting as the proper
457
    // referenced object pointer:
458

    
459
    cppintrospection::Value classInstance;
460
    if (s->s_type == REFERENCED_STATESET)
461
    {
462
        classInstance = cppintrospection::Value(dynamic_cast<ReferencedStateSet*>(s->s_thing));
463
    }
464
    else
465
    {
466
        classInstance = cppintrospection::Value(dynamic_cast<ReferencedNode*>(s->s_thing));
467
    }
468

    
469
    // the getInstanceType() method however, gives us the real type being pointed at:
470
    const cppintrospection::Type &classType = classInstance.getInstanceType();
471

    
472
    if (! classType.isDefined())
473
    {
474
        std::cout << "ERROR: oscParser cound not process message '" << path << ". cppintrospection has no data for that node." << std::endl;
475
        return 1;
476
    }
477

    
478
    spinApp &spin = spinApp::Instance();
479

    
480
    if (theMethod == "addCronScript")
481
    {
482
        // client or server?
483
        if (! lo_is_numerical_type((lo_type)types[1]))
484
        {
485
            std::string s((const char*) argv[1]);
486
            if (s[0] == 'S' || s[0] == 's')
487
            {
488
                if (! spin.getContext()->isServer())
489
                    return 0;
490
                theArgs.push_back(true);
491
            }
492
            else if (s[0] == 'C' || s[0] == 'c')
493
            {
494
                if (spin.getContext()->isServer())
495
                {
496
                    lo_message msg = lo_message_new();
497
                    for (int i = 0; i < argc; i++)
498
                    {
499
                        if (lo_is_numerical_type((lo_type) types[i]))
500
                        {
501
                            lo_message_add_float(msg, (float) lo_hires_val((lo_type) types[i], argv[i]));
502
                        }
503
                        else
504
                        {
505
                            lo_message_add_string(msg, (const char*) argv[i] );
506
                        }
507
                    }
508
                    std::vector<lo_address>::iterator addrIter;
509
                    for (addrIter = spin.getContext()->lo_txAddrs_.begin(); addrIter != spin.getContext()->lo_txAddrs_.end(); ++addrIter)
510
                    {
511
                        lo_send_message_from((*addrIter), spin.getContext()->lo_infoServ_, path, msg);
512
                    }
513
                }
514
                theArgs.push_back(false); // serverSide arg
515
            }
516
            else
517
            {
518
                std::cout << "wrong server / client specifier... must be C or S" << std::endl;
519
            }
520

    
521
        }
522
        else
523
        {
524
            std::cout << "ERROR: client or server specifier for addCronScript is not a char" << std::endl;
525
            return 1;
526
        }
527

    
528
        // label
529
        if (! lo_is_numerical_type((lo_type)types[2]))
530
        {
531
            theArgs.push_back(std::string((const char*) argv[2]));
532
        }
533
        else
534
        {
535
            std::cout << "ERROR: label for addCronScript is not a string" << std::endl;
536
            return 1;
537
        }
538

    
539
        // Script path
540
        if (! lo_is_numerical_type((lo_type) types[3]))
541
        {
542
            theArgs.push_back(std::string((const char*) argv[3]));
543
        } else {
544
            std::cout << "ERROR: script path for addCronScript is not a string" << std::endl;
545
            return 1;
546
        }
547

    
548
        // frequency
549
        if (lo_is_numerical_type((lo_type) types[4]))
550
        {
551
            theArgs.push_back((double) lo_hires_val((lo_type) types[4], argv[4]) );
552
        }
553
        else
554
        {
555
            std::cout << "ERROR: frequency for addCronScript is not a number" << std::endl;
556
            return 1;
557
        }
558

    
559
        // rest of args are comma separated and passed as a string
560
        std::stringstream params("");
561
        for (i = 5; i < argc; i++)
562
        {
563
            if (lo_is_numerical_type((lo_type) types[i]))
564
            {
565
                params << ", " << (float) lo_hires_val((lo_type) types[i], argv[i]);
566
            }
567
            else
568
            {
569
                params << ", \"" << (const char*)argv[i] << "\"";
570
            }
571
        }
572
        theArgs.push_back(params.str());
573
    }
574
    else if (theMethod == "addEventScript")
575
    {
576
        // client or server?
577
        if (! lo_is_numerical_type((lo_type) types[1]))
578
        { /// (lo_type)types[1] == LO_CHAR )
579
            std::string s((const char*) argv[1]);
580
            if (s[0] == 'S' || s[0] == 's')
581
            {
582
                if (! spin.getContext()->isServer())
583
                    return 0;
584
                theArgs.push_back(true);
585
            } 
586
            else if (s[0] == 'C' || s[0] == 'c')
587
            {
588
                if (spin.getContext()->isServer())
589
                {
590
                    lo_message msg = lo_message_new();
591
                    for (int i = 0; i < argc; i++)
592
                    {
593
                        if (lo_is_numerical_type((lo_type) types[i]))
594
                        {
595
                            lo_message_add_float(msg, (float) lo_hires_val((lo_type) types[i], argv[i]));
596
                        }
597
                        else
598
                        {
599
                            lo_message_add_string(msg, (const char*) argv[i]);
600
                        }
601
                    }
602
                    std::vector<lo_address>::iterator addrIter;
603
                    for (addrIter = spin.getContext()->lo_txAddrs_.begin(); addrIter != spin.getContext()->lo_txAddrs_.end(); ++addrIter)
604
                    {
605
                        lo_send_message_from((*addrIter), spin.getContext()->lo_infoServ_, path, msg);
606
                    }
607
                }
608
                theArgs.push_back(false); // serverSide arg
609
            }
610
        }
611
        else 
612
        {
613
            std::cout << "ERROR: client or server specifier for addCronScript is not a char" << std::endl;
614
            return 1;
615
        }
616

    
617
        // label
618
        if (! lo_is_numerical_type((lo_type) types[2])) {
619
            theArgs.push_back(std::string((const char*) argv[2]));
620
        }
621
        else
622
        {
623
            std::cout << "ERROR: label for addEventScript is not a string" << std::endl;
624
            return 1;
625
        }
626

    
627
        // event method name
628
        if (! lo_is_numerical_type((lo_type) types[3]))
629
        {
630
            theArgs.push_back(std::string((const char*) argv[3]));
631
        }
632
        else 
633
        {
634
            std::cout << "ERROR: event method name for addEventScript is not a string" << std::endl;
635
            return 1;
636
        }
637

    
638
        // Script path
639
        if (! lo_is_numerical_type((lo_type) types[4]))
640
        {
641
            theArgs.push_back(std::string((const char*) argv[4]));
642
        }
643
        else 
644
        {
645
            std::cout << "ERROR: script path for addEventScript is not a string" << std::endl;
646
            return 1;
647
        }
648

    
649
        // rest of args are comma separated and passed as a string
650
        std::stringstream params("");
651
        for (i = 5; i < argc; i++)
652
        {
653
            if (lo_is_numerical_type((lo_type) types[i]))
654
            {
655
                params << ", " << (float) lo_hires_val((lo_type) types[i], argv[i]);
656
            }
657
            else 
658
            {
659
                params << ", \"" << (const char*) argv[i] << "\"";
660
            }
661
        }
662
        theArgs.push_back(params.str());
663
    }
664
    else 
665
    {
666
        for (i = 1; i < argc; i++) 
667
        {
668
            if (lo_is_numerical_type((lo_type)types[i]))
669
            {
670
                theArgs.push_back((float) lo_hires_val((lo_type)types[i], argv[i]));
671
            } 
672
            else
673
            {
674
                theArgs.push_back((const char*) argv[i]);
675
            }
676
        }
677
    }
678

    
679
    if (spin.getContext()->isServer() &&
680
            (theMethod == "enableCronScript" || theMethod == "removeCronScript" ||
681
             theMethod == "enableEventScript" || theMethod == "removeEventScript"))
682
    {
683
        lo_message msg = lo_message_new();
684
        for (int i = 0; i < argc; i++)
685
        {
686
            if (lo_is_numerical_type((lo_type)types[i]))
687
            {
688
                lo_message_add_float(msg, (float) lo_hires_val((lo_type)types[i], argv[i]));
689
            } 
690
            else
691
            {
692
                lo_message_add_string(msg, (const char*) argv[i] );
693
            }
694
        }
695
        std::vector<lo_address>::iterator addrIter;
696
        for (addrIter = spin.getContext()->lo_txAddrs_.begin(); addrIter != spin.getContext()->lo_txAddrs_.end(); ++addrIter)
697
        {
698
            lo_send_message_from((*addrIter), spin.getContext()->lo_infoServ_, path, msg);
699
        }
700
    }
701

    
702
    bool eventScriptCalled = false;
703
    if ( s->s_type == REFERENCED_NODE )
704
    {
705
        //printf("calling eventscript...\n");
706
        eventScriptCalled = dynamic_cast<ReferencedNode*>(s->s_thing)->callEventScript( theMethod, theArgs );
707
    }
708

    
709
    if (! eventScriptCalled ) 
710
    {   // if an eventScript was hooked to theMethod, do not execute theMethod.  functionality taken over by script
711
        // invoke the method on the node, and if it doesn't work, then just forward
712
        // the message:
713
        
714
        if (0) // debug print
715
        {
716
            std::cout << "spinBaseContext got node message for: " << s->s_name << " method: " << theMethod << " args:";
717
            for (i = 0; i < argc; i++)
718
            {
719
                std::cout << " ";
720
                lo_arg_pp((lo_type)types[i], argv[i]);
721
            }
722
            std::cout << std::endl;
723
        }
724
        
725
        if (! introspector::invokeMethod(classInstance, classType, theMethod, theArgs))
726
        {
727
            //std::cout << "Ignoring method '" << theMethod << "' for [" << s->s_name << "], but forwarding message anyway..." << std::endl;
728
            // HACK: TODO: fix this
729
            if (spin.getContext()->isServer())
730
            {
731
                lo_message msg = lo_message_new();
732
                for (i = 0; i < argc; i++)
733
                {
734
                    if (lo_is_numerical_type((lo_type) types[i]))
735
                    {
736
                        lo_message_add_float(msg, (float) lo_hires_val((lo_type) types[i], argv[i]));
737
                    } else {
738
                        lo_message_add_string(msg, (const char*) argv[i]);
739
                    }
740
                }
741
                std::vector<lo_address>::iterator addrIter;
742
                for (addrIter = spin.getContext()->lo_txAddrs_.begin(); addrIter != spin.getContext()->lo_txAddrs_.end(); ++addrIter)
743
                {
744
                    lo_send_message_from((*addrIter), spin.getContext()->lo_infoServ_, path, msg);
745
                }
746
            }
747
        }
748
    }
749
    //pthread_mutex_unlock(&sceneMutex);
750
    return 1;
751
}
752

    
753
int spinBaseContext::sceneCallback(const char *path, const char *types, lo_arg **argv, int argc,
754
        void * /*data*/, void * /*user_data*/)
755
{
756
    if (0) // debug print
757
    {
758
        std::cout << "spinBaseContext got scene message: " << path << " args:";
759
        for (unsigned int i=0; i<argc; i++)
760
        {
761
            std::cout << " ";
762
            lo_arg_pp((lo_type)types[i], argv[i]);
763
        }
764
        std::cout << std::endl;
765
    }
766

    
767
    spinApp &spin = spinApp::Instance();
768
    SceneManager *sceneManager = spin.sceneManager;
769

    
770
    // make sure there is at least one argument (ie, a method to call):
771
    if (!argc) return 1;
772

    
773
    // get the method (argv[0]):
774
    std::string theMethod;
775
    if (lo_is_string_type((lo_type)types[0]))
776
        theMethod = std::string((char *)argv[0]);
777
    else
778
        return 1;
779

    
780
    //pthread_mutex_lock(&sceneMutex);
781

    
782
    // note that args start at argv[1] now:
783
    if (theMethod == "debug")
784
    {
785
        if (argc>1)
786
        {
787
            std::string debugType = (const char*)argv[1];
788
            if (debugType=="context")
789
                sceneManager->debugContext();
790
            else if (debugType=="nodes")
791
                sceneManager->debugNodes();
792
            else if (debugType=="statesets")
793
                sceneManager->debugStateSets();
794
            else if (debugType=="scenegraph")
795
                sceneManager->debugSceneGraph();
796
            else if (debugType=="spatosc")
797
            {
798
                #ifdef WITH_SPATOSC
799
                if (spinApp::Instance().hasAudioRenderer)
800
                    spinApp::Instance().audioScene->debugPrint();
801
                else
802
                    std::cout << "SpatOSC not enabled for this SPIN application" << std::endl;
803
                #endif
804
            }
805

    
806
            // forward debug message to all clients:
807
            SCENE_MSG("ss", "debug", (const char*)argv[1]);
808
        }
809
        else
810
        {
811
            sceneManager->debug();
812
            SCENE_MSG("s", "debug");
813
        }
814
    }
815
    else if (theMethod == "clear")
816
        sceneManager->clear();
817
    else if (theMethod == "clearUsers")
818
        sceneManager->clearUsers();
819
    else if (theMethod == "clearStates")
820
        sceneManager->clearStates();
821
    else if (theMethod == "userRefresh")
822
    {
823
        if (spin.getContext()->isServer())
824
        {
825
            SCENE_MSG("s", "userRefresh");
826
        }
827
        else
828
        {
829
            spin.SceneMessage("sss", "createNode", spin.getUserID().c_str(), "UserNode", LO_ARGS_END);
830
            
831
            // In the case of a client, we also need to check if the current 
832
            // userNode is actually attached. The deleteNode / clear / whatever
833
            // may have detached it from the scenegraph:
834
            if (!sceneManager->worldNode->containsNode(spin.userNode.get()))
835
            {
836
                std::cout << "calling attach on userNode (newparent=" << spin.userNode->newParent->s_name << std::endl;
837
                spin.userNode->newParent = WORLD_SYMBOL;
838
                spin.userNode->attach();
839
            }
840
            
841
            // if the server sends a userRefresh, it's possible that it has
842
            // only recently come online, so we need to re-subsrcibe:
843
            spinClientContext *clientContext = dynamic_cast<spinClientContext*>(spin.getContext());
844
            clientContext->subscribe();
845
        }
846
    }
847
    else if (theMethod == "refresh")
848
        sceneManager->refreshAll();
849
    else if (theMethod == "refreshSubscribers")
850
    {
851
        if (spin.getContext()->isServer())
852
        {
853
            spinServerContext *server = dynamic_cast<spinServerContext*>(spin.getContext());
854
            server->refreshSubscribers();
855
        }
856
    }
857
    else if (theMethod == "getNodeList")
858
        sceneManager->sendNodeList("*");
859
    else if ((theMethod == "nodeList") && (argc>2))
860
    {
861
        for (int i = 2; i < argc; i++)
862
        {
863
            if (strcmp((char*) argv[i],"NULL") != 0)
864
                sceneManager->createNode((char*)argv[i], (char*)argv[1]);
865
        }
866
    }
867
    else if ((theMethod=="stateList") && (argc>2))
868
    {
869
        for (int i=2; i<argc; i++)
870
        {
871
            if (strcmp((char*) argv[i],"NULL") != 0)
872
                sceneManager->createStateSet((char*) argv[i], (char*) argv[1]);
873
        }
874
    }
875
    else if ((theMethod == "exportScene") && (argc==3))
876
        sceneManager->exportScene((char*) argv[1], (char*) argv[2]);
877
    else if ((theMethod == "load") && (argc==2))
878
        sceneManager->loadXML((char*) argv[1]);
879
    else if ((theMethod == "save") && (argc==2))
880
        sceneManager->saveXML((char*) argv[1]);
881
    else if ((theMethod == "saveAll") && (argc==2))
882
        sceneManager->saveXML((char*) argv[1], true);
883
    else if ((theMethod == "saveUsers") && (argc==2))
884
        sceneManager->saveUsers((char*) argv[1]);
885
    else if ((theMethod == "createNode") && (argc==3))
886
        sceneManager->createNode((char*) argv[1], (char*) argv[2]);
887
    else if ((theMethod == "createStateSet") && (argc==3))
888
        sceneManager->createStateSet((char*) argv[1], (char*) argv[2]);
889
    else if ((theMethod == "createStateSet") && (argc==2))
890
        sceneManager->createStateSet((char*) argv[1]);
891
    else if ((theMethod == "setWorldStateSet") && (argc==2))
892
        sceneManager->setWorldStateSet((char*) argv[1]);
893
    else if ((theMethod == "deleteNode") && (argc==2))
894
        sceneManager->deleteNode((char*) argv[1]);
895
    else if ((theMethod == "deleteGraph") && (argc==2))
896
        sceneManager->deleteGraph((char*) argv[1]);
897
    else if ((theMethod == "setNotifyLevel") && (argc==2))
898
    {
899
        int lvl = (int) lo_hires_val((lo_type)types[1], argv[1]);
900
        if ((lvl>=0) && (lvl<=6))
901
        {
902
            osg::setNotifyLevel((osg::NotifySeverity)lvl);
903
            std::cout << "setNotifyLevel " << osg::getNotifyLevel() << std::endl;
904
        }
905
        SCENE_MSG("si", "setNotifyLevel", (int)osg::getNotifyLevel());
906
    }
907
    else if ((theMethod == "optimize") && (argc==2))
908
    {
909
        osgUtil::Optimizer optimizer;
910
        if (std::string((char*) argv[1]) == "all")
911
        {
912
            std::cout << "Optimizing scene (all)" << std::endl;
913
            pthread_mutex_lock(&sceneMutex); 
914
            optimizer.optimize(sceneManager->worldNode.get(), osgUtil::Optimizer::ALL_OPTIMIZATIONS);
915
            pthread_mutex_unlock(&sceneMutex);
916
        }
917
        else
918
        {
919
            std::cout << "Optimizing scene" << std::endl;
920
            pthread_mutex_lock(&sceneMutex); 
921
            optimizer.optimize(sceneManager->worldNode.get());
922
            pthread_mutex_unlock(&sceneMutex); 
923
        }
924
        SCENE_MSG("ss", "optimize", (char*)argv[1]);
925
    }
926
    else if (theMethod == "spatosc")
927
    {
928
#ifdef WITH_SPATOSC
929
        if (spinApp::Instance().hasAudioRenderer)
930
        {
931
            if ((argc==4) && (std::string((char*)argv[1])=="setDefaultDistanceFactor"))
932
            {
933
                double factor = (double) lo_hires_val((lo_type)types[2], argv[2]);
934
                bool updateExisting = (bool) lo_hires_val((lo_type)types[3], argv[3]);
935
                spin.audioScene->setDefaultDistanceFactor(factor, updateExisting);
936
            }
937
            if ((argc==4) && (std::string((char*)argv[1])=="setDefaultDopplerFactor"))
938
            {
939
                double factor = (double) lo_hires_val((lo_type)types[2], argv[2]);
940
                bool updateExisting = (bool) lo_hires_val((lo_type)types[3], argv[3]);
941
                spin.audioScene->setDefaultDopplerFactor(factor, updateExisting);
942
            }
943
            if ((argc==4) && (std::string((char*)argv[1])=="setDefaultRolloffFactor"))
944
            {
945
                double factor = (double) lo_hires_val((lo_type)types[2], argv[2]);
946
                bool updateExisting = (bool) lo_hires_val((lo_type)types[3], argv[3]);
947
                spin.audioScene->setDefaultRolloffFactor(factor, updateExisting);
948
            }
949
        }
950
#endif
951
    }
952
    else
953
    {
954
        // FIXME: this used to rebroadcast messages that did not match command
955
#if 0
956
        spinApp &spin = spinApp::Instance();
957
        if (spin.sceneManager->isServer())
958
        {
959
            lo_message msg = lo_message_new();
960
            for (int i=0; i<argc; i++)
961
            {
962
                if (lo_is_numerical_type((lo_type)types[i]))
963
                {
964
                    lo_message_add_float(msg, (float) lo_hires_val((lo_type)types[i], argv[i]));
965
                } else {
966
                    lo_message_add_string(msg, (const char*) argv[i] );
967
                }
968
            }
969
            lo_send_message_from(spin.sceneManager->txAddr, spin.sceneManager->txServ, path, msg);
970
            //std::cout << "Unknown OSC command: " << path << " " << theMethod << " (with " << argc-1 << " args), but forwarding the message anyway." << std::endl;
971
        } else {
972
            //std::cout << "Unknown OSC command: " << path << " " << theMethod << " (with " << argc-1 << " args)" << std::endl;
973
        }
974
#endif
975
    }
976

    
977
    //pthread_mutex_unlock(&sceneMutex);
978

    
979
    return 1;
980
}
981

    
982
int spinBaseContext::logCallback(const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data)
983
{
984
    spinLog *log = (spinLog*) user_data;
985

    
986
    *log << path << ' ' << types << ' ';
987
    for (int i = 0; i < argc; i++)
988
    {
989
        if (lo_is_numerical_type((lo_type) types[i]))
990
        {
991
            *log << (float) lo_hires_val((lo_type) types[i], argv[i]);
992
        } else {
993
            *log << (const char*) argv[i];
994
        }
995
        *log << ' ';
996
    }
997
    *log << std::endl;
998

    
999
    return 1;
1000
}
1001

    
1002
int spinBaseContext::debugCallback(const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data)
1003
{
1004
    printf("************ oscCallback_debug() got message: %s\n", (char*)path);
1005
    printf("user_data: %s\n", (char*) user_data);
1006
    for (int i = 0; i < argc; i++)
1007
    {
1008
        printf("arg %d '%c' ", i, types[i]);
1009
        lo_arg_pp((lo_type) types[i], argv[i]);
1010
        printf("\n");
1011
    }
1012
    printf("\n");
1013
    fflush(stdout);
1014
    return 1;
1015
}
1016

    
1017
void spinBaseContext::oscParser_error(int num, const char *msg, const char *path)
1018
{
1019
    printf("OSC (liblo) error %d in path %s: %s\n", num, path, msg);
1020
    fflush(stdout);
1021
}
1022

    
1023
void spinBaseContext::createServers()
1024
{
1025
    using boost::lexical_cast;
1026
    using std::string;
1027

    
1028
    lo_tcpRxServer_ = lo_server_new_with_proto(tcpPort_.c_str(), LO_TCP, oscParser_error);
1029
    if (lo_tcpRxServer_ == 0)
1030
    {
1031
        if (canAutoAssignPorts())
1032
        {
1033
            // liblo will try a random free port if the default failed
1034
            std::cerr << "TCP receiver port " << tcpPort_ << " failed; trying a random port" << std::endl;
1035
            lo_tcpRxServer_ = lo_server_new_with_proto(NULL, LO_TCP, oscParser_error);
1036
        } else {
1037
            std::cerr << "TCP receiver port " << tcpPort_ << " failed; SPIN was provided this port manually, so it will not attempt to use a random port. Quitting." << std::endl;
1038
            exit(0);
1039
        }
1040
    }
1041

    
1042
    std::vector<lo_address>::iterator it;
1043
    for (it = lo_rxAddrs_.begin(); it != lo_rxAddrs_.end(); ++it)
1044
    {
1045
        lo_server tmpServ;
1046
        if (isMulticastAddress(lo_address_get_hostname(*it)))
1047
        {
1048
            tmpServ = lo_server_new_multicast(lo_address_get_hostname(*it), lo_address_get_port(*it), oscParser_error);
1049
            if (tmpServ == 0)
1050
            {
1051
                std::cerr << "Multicast server creation on port " << lo_address_get_port(*it) << " failed, trying a random port" << std::endl;
1052
                std::string addr(lo_address_get_hostname(*it));
1053
                tmpServ = lo_server_new_multicast(addr.c_str(), NULL, oscParser_error);
1054
                lo_address_free(*it);
1055
                (*it) = lo_address_new(addr.c_str(), lexical_cast<string>(lo_server_get_port(tmpServ)).c_str());
1056
            }
1057
        }
1058
        else
1059
        {
1060
            tmpServ = lo_server_new(lo_address_get_port(*it), oscParser_error);
1061
            if (tmpServ == 0)
1062
            {
1063
                std::cerr << "UDP listener creation on port " << lo_address_get_port(*it) << " failed, trying a random port" << std::endl;
1064
                tmpServ = lo_server_new(NULL, oscParser_error);
1065
                std::string addr(lo_address_get_hostname(*it));
1066
                lo_address_free(*it);
1067
                (*it) = lo_address_new(addr.c_str(), lexical_cast<string>(lo_server_get_port(tmpServ)).c_str());
1068
            }
1069
        }
1070
        lo_rxServs_.push_back(tmpServ);
1071
    }
1072

    
1073
    // set up infoPort listener thread:
1074
    if (isMulticastAddress(lo_address_get_hostname(lo_infoAddr)))
1075
    {
1076
        lo_infoServ_ = lo_server_new_multicast(lo_address_get_hostname(lo_infoAddr), lo_address_get_port(lo_infoAddr), oscParser_error);
1077
        if (lo_infoServ_ == 0)
1078
        {
1079
            std::cerr << "Multicast info server creation on port " << lo_address_get_port(lo_infoAddr) << 
1080
                " failed, trying a random port" << std::endl;
1081
            std::string addr(lo_address_get_hostname(lo_infoAddr));
1082
            lo_address_free(lo_infoAddr);
1083
            lo_infoServ_ = lo_server_new_multicast(addr.c_str(), NULL, oscParser_error);
1084
            lo_infoAddr = lo_address_new(addr.c_str(), lexical_cast<string>(lo_server_get_port(lo_infoServ_)).c_str());
1085
        }
1086
    } 
1087
    else if (isBroadcastAddress(lo_address_get_hostname(lo_infoAddr)))
1088
    {
1089
        lo_infoServ_ = lo_server_new(lo_address_get_port(lo_infoAddr), oscParser_error);
1090
        if (lo_infoServ_ == 0)
1091
        {
1092
            std::cerr << "Info server creation on port " << lo_address_get_port(lo_infoAddr) << 
1093
                " failed, trying a random port" << std::endl;
1094
            std::string addr(lo_address_get_hostname(lo_infoAddr));
1095
            lo_address_free(lo_infoAddr);
1096
            lo_infoServ_ = lo_server_new(NULL, oscParser_error);
1097
            lo_infoAddr = lo_address_new(addr.c_str(), lexical_cast<string>(lo_server_get_port(lo_infoServ_)).c_str());
1098
        }
1099
        int sock = lo_server_get_socket_fd(lo_infoServ_);
1100
        int sockopt = 1;
1101
        setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &sockopt, sizeof(sockopt));
1102
    } 
1103
    else 
1104
    {
1105
        lo_infoServ_ = lo_server_new(lo_address_get_port(lo_infoAddr), oscParser_error);
1106
        if (lo_infoServ_ == 0)
1107
        {
1108
            std::cerr << "Info server creation on port " << lo_address_get_port(lo_infoAddr) << 
1109
                " failed, trying a random port" << std::endl;
1110
            std::string addr(lo_address_get_hostname(lo_infoAddr));
1111
            lo_address_free(lo_infoAddr);
1112
            lo_infoServ_ = lo_server_new(NULL, oscParser_error);
1113
            lo_infoAddr = lo_address_new(addr.c_str(), lexical_cast<string>(lo_server_get_port(lo_infoServ_)).c_str());
1114
        }
1115
    }
1116
}
1117

    
1118
} // end of namespace spin
1119