root/trunk/libffado/src/libosc/OscNode.cpp

Revision 461, 8.6 kB (checked in by ppalmers, 16 years ago)

- Osc requests now don't require a perfect path match to work.

e.g. an OscNode? at path '/X/Y' will now handle OscMessages? for '/X/Y/Z'

if 'Z' is not registered as a child OscNode? of '/X/Y'

Line 
1 /*
2  * Copyright (C) 2005-2007 by Pieter Palmers
3  *
4  * This file is part of FFADO
5  * FFADO = Free Firewire (pro-)audio drivers for linux
6  *
7  * FFADO is based upon FreeBoB
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License version 2.1, as published by the Free Software Foundation;
12  *
13  * This library 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 GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  * MA 02110-1301 USA
22  */
23
24 #include "OscNode.h"
25 #include "OscMessage.h"
26 #include "OscResponse.h"
27 #include <assert.h>
28
29 namespace OSC {
30
31 IMPL_DEBUG_MODULE( OscNode, OscNode, DEBUG_LEVEL_VERBOSE );
32
33 OscNode::OscNode()
34     : m_oscBase("")
35     , m_oscAutoDelete(false)
36 {}
37
38 OscNode::OscNode(string b)
39     : m_oscBase(b)
40     , m_oscAutoDelete(false)
41 {}
42
43 OscNode::OscNode(string b, bool autodelete)
44     : m_oscBase(b)
45     , m_oscAutoDelete(autodelete)
46 {}
47
48 OscNode::~OscNode() {
49     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Removing child nodes\n");
50
51     for ( OscNodeVectorIterator it = m_ChildNodes.begin();
52       it != m_ChildNodes.end();
53       ++it )
54     {
55         if((*it)->doOscNodeAutoDelete()) {
56             debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " Auto-deleting child node %s\n",
57                          (*it)->getOscBase().c_str());
58             delete (*it);
59         }
60     }
61 }
62
63 void
64 OscNode::setVerboseLevel(int l) {
65     setDebugLevel(l);
66     for ( OscNodeVectorIterator it = m_ChildNodes.begin();
67       it != m_ChildNodes.end();
68       ++it )
69     {
70         (*it)->setVerboseLevel(l);
71     }
72 }
73
74 // generic message processing
75 OscResponse
76 OscNode::processOscMessage(string path, OscMessage *m)
77 {
78     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) {%s} MSG: %s\n", this, m_oscBase.c_str(), path.c_str());
79
80     // delete leading slash
81     if(path.find_first_of('/')==0) path=path.substr(1,path.size());
82
83     // delete trailing slash
84     if(path.find_last_of('/')==path.size()-1) path=path.substr(0,path.size()-1);
85
86     // continue processing
87     int firstsep=path.find_first_of('/');
88
89     if (firstsep == -1) {
90         OscResponse retVal;
91
92         // process the message
93         m->setPath(""); // handled by the node itself
94         retVal=processOscMessage(m);
95
96         if(retVal.isHandled()) {
97             return retVal; // completely handled
98         } else { // (partially) unhandled
99             return processOscMessageDefault(m, retVal);
100         }
101
102     } else { // it targets a deeper node
103         OscResponse retVal;
104        
105         string newpath=path.substr(firstsep+1);
106         int secondsep=newpath.find_first_of('/');
107         string newbase;
108         if (secondsep==-1) {
109             newbase=newpath;
110         } else {
111             newbase=path.substr(firstsep+1,secondsep);
112         }
113
114         // first try to find a child node that might be able
115         // to handle this.
116         // NOTE: the current model allows only one node to
117         //       handle a request, and then the default
118         //       handler.
119         for ( OscNodeVectorIterator it = m_ChildNodes.begin();
120           it != m_ChildNodes.end();
121           ++it )
122         {
123             if((*it)->getOscBase() == newbase) {
124                 return (*it)->processOscMessage(newpath,m);
125             }
126         }
127        
128         // The path is not registered as a child node.
129         // This node should handle it
130         debugOutput( DEBUG_LEVEL_VERBOSE, "Child node %s not found \n",newbase.c_str());
131
132         m->setPath(newpath); // the remaining portion of the path
133         retVal=processOscMessage(m);
134
135         if(retVal.isHandled()) {
136             return retVal; // completely handled
137         }
138         // (partially) unhandled
139         return processOscMessageDefault(m, retVal);
140     }
141     return OscResponse(OscResponse::eError);
142 }
143
144 // default handlers
145
146 // overload this to make a node process a message
147 OscResponse
148 OscNode::processOscMessage(OscMessage *m)
149 {
150     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) {%s} DEFAULT PROCESS: %s\n", this, m_oscBase.c_str(), m->getPath().c_str());
151     m->print();
152     return OscResponse(OscResponse::eUnhandled); // handled but no response
153 }
154
155 // this provides support for messages that are
156 // supported by all nodes
157 OscResponse
158 OscNode::processOscMessageDefault(OscMessage *m, OscResponse r)
159 {
160     unsigned int nbArgs=m->nbArguments();
161     if (nbArgs>=1) {
162         OscArgument arg0=m->getArgument(0);
163         if(arg0.isString()) { // first argument should be a string command
164             string cmd=arg0.getString();
165             debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) CMD? %s\n", this, cmd.c_str());
166             if(cmd == "list") {
167                 debugOutput( DEBUG_LEVEL_VERBOSE, "Listing node children...\n");
168                 OscMessage rm=oscListChildren(r.getMessage());
169                 return OscResponse(rm);
170             }
171         }
172     }
173     // default action: don't change response
174     return r;
175 }
176
177 OscMessage
178 OscNode::oscListChildren(OscMessage m) {
179
180     for ( OscNodeVectorIterator it = m_ChildNodes.begin();
181       it != m_ChildNodes.end();
182       ++it )
183     {
184         m.addArgument((*it)->getOscBase());
185     }
186     m.print();
187     return m;
188 }
189
190 // child management
191
192 bool
193 OscNode::addChildOscNode(OscNode *n)
194 {
195     assert(n);
196
197     debugOutput( DEBUG_LEVEL_VERBOSE, "Adding child node %s\n",n->getOscBase().c_str());
198     for ( OscNodeVectorIterator it = m_ChildNodes.begin();
199       it != m_ChildNodes.end();
200       ++it )
201     {
202         if(*it == n) {
203             debugOutput( DEBUG_LEVEL_VERBOSE,
204                 "Child node %s already registered\n",
205                 n->getOscBase().c_str());
206             return false;
207         }
208     }
209     m_ChildNodes.push_back(n);
210     return true;
211 }
212
213 /**
214  * Add a child node under a certain path.
215  * e.g. if you have a node with base 'X' and
216  * you want it to be at /base/level1/level2/X
217  * you would add it using this function, by
218  *  addChildOscNode(n, "/base/level1/level2")
219  *
220  * @param n
221  * @param
222  * @return
223  */
224 bool
225 OscNode::addChildOscNode(OscNode *n, string path)
226 {
227     debugOutput( DEBUG_LEVEL_VERBOSE, "add node to: %s\n",path.c_str());
228
229     // delete leading slashes
230     if(path.find_first_of('/')==0) path=path.substr(1,path.size());
231
232     // delete trailing slashes
233     if(path.find_last_of('/')==path.size()-1) path=path.substr(0,path.size()-1);
234
235     // continue processing
236     int firstsep=path.find_first_of('/');
237
238     if (firstsep == -1) {
239         return addChildOscNode(n);
240     } else { // it targets a deeper node
241         string newpath=path.substr(firstsep+1);
242         int secondsep=newpath.find_first_of('/');
243         string newbase;
244         if (secondsep==-1) {
245             newbase=newpath;
246         } else {
247             newbase=path.substr(firstsep+1,secondsep);
248         }
249
250         // if the path is already present, find it
251         for ( OscNodeVectorIterator it = m_ChildNodes.begin();
252           it != m_ChildNodes.end();
253           ++it )
254         {
255             if((*it)->getOscBase() == newbase) {
256                 return (*it)->addChildOscNode(n,newpath);
257             }
258         }
259         debugOutput( DEBUG_LEVEL_VERBOSE, "node %s not found, creating auto-node\n",newbase.c_str());
260
261         OscNode *autoNode=new OscNode(newbase,true);
262
263         // add the auto-node to this node
264         m_ChildNodes.push_back(autoNode);
265
266         // add the child to the node
267         return autoNode->addChildOscNode(n,newpath);
268     }
269     return false;
270 }
271
272 bool
273 OscNode::removeChildOscNode(OscNode *n)
274 {
275     assert(n);
276     debugOutput( DEBUG_LEVEL_VERBOSE, "Removing child node %s\n",n->getOscBase().c_str());
277
278     for ( OscNodeVectorIterator it = m_ChildNodes.begin();
279       it != m_ChildNodes.end();
280       ++it )
281     {
282         if(*it == n) {
283             m_ChildNodes.erase(it);
284             if((*it)->doOscNodeAutoDelete()) {
285                 debugOutput( DEBUG_LEVEL_VERBOSE, " Auto-deleting child node %s\n",n->getOscBase().c_str());
286                 delete (*it);
287             }
288             return true;
289         }
290     }
291
292     debugOutput( DEBUG_LEVEL_VERBOSE, "Child node %s not found \n",n->getOscBase().c_str());
293
294     return false; //not found
295 }
296
297 void
298 OscNode::printOscNode()
299 {
300     printOscNode("");
301 }
302
303 void
304 OscNode::printOscNode(string path)
305 {
306     debugOutput( DEBUG_LEVEL_NORMAL, "%s/%s\n",
307                  path.c_str(),getOscBase().c_str());
308
309     for ( OscNodeVectorIterator it = m_ChildNodes.begin();
310       it != m_ChildNodes.end();
311       ++it )
312     {
313         (*it)->printOscNode(path+"/"+getOscBase());
314     }
315 }
316
317 } // end of namespace OSC
Note: See TracBrowser for help on using the browser.