00001 /* 00002 * WOscLib, an object oriented OSC library. 00003 * Copyright (C) 2005 Uli Clemens Franke, Weiss Engineering LTD, Switzerland. 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Lesser General Public 00007 * License as published by the Free Software Foundation; either 00008 * version 2.1 of the License, or (at your option) any later version. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Lesser General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public 00016 * License along with this library; if not, write to the Free Software 00017 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00018 * 00019 * For details see lgpl.txt 00020 * 00021 * Weiss Engineering LTD. 00022 * Florastrass 42 00023 * 8610 Uster 00024 * Switzerland 00025 * 00026 * uli.franke@weiss.ch 00027 */ 00028 00029 /** WOscReceiver source file. 00030 * \file 00031 * 00032 * $Author: cls-nebadje $ ( \ref _UcfWOscLib ) 00033 * $Date: 2007-11-30 00:27:08 $ 00034 * $Revision: 1.6 $ 00035 * 00036 * Copyright (c) Weiss Engineering Ltd 00037 * 00038 */ 00039 #include "WOscReceiver.h" 00040 00041 #if WOSC_USE_PRIORITY_QUEUE == 1 00042 /** Constructor. 00043 * Constructs a receiver and initilizes its priority- 00044 * queue. 00045 * 00046 * \param initialQueueCapacity 00047 * Optional size of priority queue. Grows automatically quadratically if 00048 * too small. 00049 * 00050 * \param systemTime 00051 * Optional system-time object (inherited class from WOscSystemTime) which 00052 * supplies the receiver with system dependent time information. Passed 00053 * objects will be deleted by the receiver object destructor. 00054 */ 00055 WOscReceiver::WOscReceiver(int initialQueueCapacity, 00056 WOscSystemTime* systemTime) : WOscPriorityQueue(initialQueueCapacity) 00057 #else 00058 /** Constructor. 00059 * Constructs a receiver and initilizes its priority- 00060 * queue. 00061 * 00062 * \param systemTime 00063 * Optional system-time object (inherited class from WOscSystemTime) which 00064 * supplies the receiver with system dependent time information. Passed 00065 * objects will be deleted by the receiver object destructor. 00066 */ 00067 WOscReceiver::WOscReceiver(WOscSystemTime* systemTime) 00068 #endif // #if WOSC_USE_PRIORITY_QUEUE == 1 00069 /* init inherited class */ 00070 { 00071 /* reset address space */ 00072 m_addrSpaceRootContainer = NULL; 00073 00074 /* set system time generator to NULL when not specified*/ 00075 m_systemTime = systemTime; 00076 } 00077 00078 /** Cleans up the receiver. 00079 * All elements which are not removed from the priority-queue 00080 * get deleted automatically. 00081 */ 00082 WOscReceiver::~WOscReceiver() 00083 { 00084 if ( m_systemTime ) 00085 delete m_systemTime; 00086 } 00087 00088 /** Sets the address-space of the WOscReceiver. 00089 * The address-space can be modified dynamically and externally, 00090 * desired, if the root-container remains valid. 00091 * 00092 * \param addrSpaceRootContainer 00093 * Root-container. Can not be a child. 00094 * 00095 * \throws WOscException 00096 * If root-container is not root. 00097 * 00098 * \remarks 00099 * If currently no address-space should be available, pass NULL. 00100 * This disables the invokation of messages. 00101 */ 00102 void 00103 WOscReceiver::SetAddressSpace(WOscContainer* addrSpaceRootContainer) 00104 { 00105 if ( ! addrSpaceRootContainer->IsRoot() ) 00106 throw WOscException(ERR_NOT_ROOT_CONTAINER, __FILE__, __LINE__); 00107 m_addrSpaceRootContainer = addrSpaceRootContainer; 00108 } 00109 00110 /** Returns the internal address-space reference. 00111 * 00112 * \returns 00113 * Internal address-space, if there is none NULL. 00114 */ 00115 WOscContainer* 00116 WOscReceiver::GetAddressSpace() 00117 { 00118 return m_addrSpaceRootContainer; 00119 } 00120 00121 /** Parses a received packet, constructs messages and/or bundles of it and 00122 * passes it to the invocation-engine, which scans the address-space for 00123 * matching methods, takes care of time-tags and queues bundles, which have to 00124 * be delayed, in the priority-queue. 00125 * 00126 * \param data 00127 * Binary OSC-packet data. 00128 * 00129 * \param dataLen 00130 * Length of binary OSC-packet data. 00131 * 00132 * \param networkReturnAddress 00133 * Network packet origin. 00134 * 00135 * \throws 00136 * None, but if an internal exception occurs, the user-defined 00137 * exception-handler is called, see below. 00138 * 00139 * \see 00140 * handleOffendingPackets(const char* const data, int dataLen, WOscNetReturn networkReturnAddress, WOscException* exception) 00141 */ 00142 void 00143 WOscReceiver::NetworkReceive(const char* data, int dataLen, 00144 WOscNetReturn* networkReturnAddress) 00145 { 00146 /* check data length to not read wrong data 00147 * when guessing OSC-object 00148 */ 00149 if ( dataLen < 1 ) 00150 return; 00151 00152 // Guess if bundle or message. If bundle, the return address gets managed 00153 // by the bundle and priority-queue. If not, the return-address has to be 00154 // deleted manually after the message has been processed. 00155 if ( data[0] == '#' ){ 00156 // probably a bundle. We try to parse it here. 00157 try { 00158 ProcessBundle(data, dataLen, networkReturnAddress); 00159 } catch(const WOscException& exception) { 00160 // call user defined exception handler 00161 HandleOffendingPackets(data, dataLen, exception); 00162 // clean up (no message constructed -> netreturn has to be deleted manually 00163 networkReturnAddress->RemoveParent(); 00164 return; 00165 } 00166 } else { 00167 // probably a message -> invoke 00168 try{ 00169 ProcessMessage(data, dataLen, networkReturnAddress); 00170 } catch(const WOscException& exception) { 00171 // call user defined exception handler 00172 HandleOffendingPackets(data, dataLen, exception); 00173 // clean up (no message constructed -> netreturn has to be deleted manually 00174 networkReturnAddress->RemoveParent(); 00175 return; 00176 } 00177 00178 /* clean up return address. 00179 * if received OSC-object is not a bundle the network-return address 00180 * is not managed by it and has to be deleted manually. 00181 */ 00182 networkReturnAddress->RemoveParent(); 00183 } 00184 } 00185 00186 /** Internal bundle processing. 00187 * Checks time-tag of bundle, if not ready it is queued in 00188 * the priority queue. Else it is unbundled, the contained 00189 * messages are invoked and internal bundles are passed to 00190 * to processBundle(WOscBundle* bundle) again (recursion). 00191 * 00192 * \param bundle 00193 * The bundle which has to be processed. 00194 * 00195 * \throws WOscException 00196 * When parsing bundles or messages. 00197 * 00198 */ 00199 void 00200 WOscReceiver::ProcessBundle(const char* data, unsigned int len, 00201 WOscNetReturn* networkReturnAddress) 00202 { 00203 WOscBundleParser bundle; 00204 bundle.Parse(data, len); 00205 00206 #if WOSC_USE_PRIORITY_QUEUE == 1 00207 // check time-tag 00208 if ( bundle.GetTimeTag() <= WOscTimeTag::GetCurrentTime(m_systemTime) ) { 00209 #endif 00210 const char* buffer; 00211 unsigned int bufferLength; 00212 while ( bundle.GetNextItem(&buffer, &bufferLength) ) { 00213 if ( bufferLength > 0 ) { 00214 // check if bundle in bundle 00215 if ( buffer[1] == '#' ) { 00216 ProcessBundle(buffer, bufferLength, networkReturnAddress); 00217 } else { 00218 ProcessMessage(buffer, bufferLength, networkReturnAddress); 00219 } 00220 } else { 00221 // buffer length zero throw exeption 00222 } 00223 } 00224 // we processed this bundle so we remove the return address. 00225 networkReturnAddress->RemoveParent(); 00226 00227 #if WOSC_USE_PRIORITY_QUEUE == 1 00228 } else { 00229 // the bundle is not ready, queue it in the priority-queue. 00230 InsertItem( new WOscQueueBundle(bundle, networkReturnAddress) ); 00231 } 00232 #endif 00233 } 00234 /** Internal message processing. 00235 * The message is passed to the address-space and a 00236 * call-back-list is created and invoked. 00237 * 00238 * \param msg 00239 * Message to be processed. 00240 * 00241 * \param networkReturnAddress 00242 * The network-origin of this message. 00243 * 00244 * \throws WOscException 00245 * Message internal exceptions, there should be none. 00246 * Internal exceptions point to library bugs. 00247 * 00248 * \remarks 00249 * The message passed to this function will be deleted when 00250 * processed. 00251 */ 00252 void 00253 WOscReceiver::ProcessMessage(const char* data, unsigned int len, const WOscNetReturn* networkReturnAddress) 00254 { 00255 WOscMessage* msg = new WOscMessage(data, len); 00256 00257 try { 00258 WOscTimeTag currentTime = WOscTimeTag::GetCurrentTime(m_systemTime); 00259 00260 if ( m_addrSpaceRootContainer ){ 00261 /* get matching methods */ 00262 m_callbackList.Reset(); 00263 m_addrSpaceRootContainer->DispatchMessage(m_callbackList, msg); 00264 // check if matching methods found 00265 if ( m_callbackList.GetCount() == 0 ) { 00266 // if not, handle message in special handler 00267 HandleNonmatchedMessages(msg, networkReturnAddress); 00268 } else { 00269 m_callbackList.Invoke(msg, currentTime, networkReturnAddress); 00270 } 00271 } 00272 } catch (const WOscException& exception) { 00273 // message processing failed. delete message and throw exception to 00274 // next handler. 00275 delete msg; 00276 throw exception; 00277 } 00278 // message done, clean up message 00279 delete msg; 00280 } 00281 00282 #if WOSC_USE_PRIORITY_QUEUE == 1 00283 /** Keep the internal priority-queue running. 00284 * If there are any ready-to-process-elements, 00285 * they are removed from the priority-queue and 00286 * processed. 00287 * 00288 * \throws WOscException 00289 * Bundle or message internal exceptions, there should be none. 00290 * Internal exceptions point to library bugs. 00291 * 00292 * \remarks 00293 * Call this method perodically to keep the priority-queue running. 00294 */ 00295 void 00296 WOscReceiver::ManagePriorityQueue() 00297 { 00298 // run as long there is something ready 00299 while ( (GetEarliestTimeTag() < WOscTimeTag::GetCurrentTime(m_systemTime) ) && (GetNumItems() > 0) ) { 00300 WOscQueueBundle* bundle = NULL; 00301 WOscNetReturn* netReturn = NULL; 00302 try { 00303 // get element from queue 00304 WOscQueueBundle* bundle = static_cast<WOscQueueBundle*>(RemoveEarliestItem()); 00305 // remove network return address from it 00306 netReturn = bundle->RemoveNetworkReturnAddress(); 00307 // process it (if it runs through properly also the reference 00308 // counter of netReturn gets decremented 00309 ProcessBundle(bundle->GetData(), bundle->GetDataLen(), netReturn); 00310 00311 } catch(const WOscException& exception) { 00312 // call user defined exception handler 00313 HandleOffendingPackets(NULL, 0, exception); 00314 if ( netReturn ) 00315 netReturn->RemoveParent(); 00316 } 00317 delete bundle; 00318 } 00319 } 00320 00321 #endif // #if WOSC_USE_PRIORITY_QUEUE == 1