Jamoma API  0.6.0.a19
WebSocket.cpp
Go to the documentation of this file.
1 /**
2  * @file WebSocket.cpp
3  * @ingroup modularWebSocket
4  * @brief It's the WebSocket protocol
5  * @authors Laurent Garnier
6  *
7  * @copyright © 2013, Laurent Garnier @n
8  * This code is licensed under the terms of the "New BSD License" @n
9  * http://creativecommons.org/licenses/BSD/
10  *
11  * @details Use cross platform libjson library from http://libjson.sourceforge.net/
12  */
13 
14 #include "TTProtocol.h"
15 #include "WebSocket.h"
16 
17 #define thisTTClass WebSocket
18 #define thisTTClassName "WebSocket"
19 #define thisTTClassTags "network, protocol, websocket"
20 
21 #define thisProtocolVersion "0.1"
22 #define thisProtocolAuthor "Laurent Garnier"
23 #define thisProtocolGet YES
24 #define thisProtocolSet YES
25 #define thisProtocolListen YES
26 #define thisProtocolDiscover YES
27 #define thisProtocolDiscoverAll YES
28 
29 void addChildToJson(JSONNode* jsonNode, TTSymbol childName, TTValue value);
30 void addAttributeToJson(TTObjectBasePtr param, JSONNode* jsonNode, TTSymbol attrName);
31 void parseChildren(JSONNode* jsonNode, TTNodePtr ttNode, TTBoolean isFirstParsing);
32 
33 extern "C" TT_EXTENSION_EXPORT TTErr TTLoadJamomaExtension_WebSocket(void)
34 {
35  TTFoundationInit();
36  WebSocket::registerClass();
37  return kTTErrNone;
38 }
39 
41 mWaitThread(NULL),
42 mAnswerManager(NULL),
43 mSenderManager(NULL)
44 {
46 
49 
50  addMessageWithArguments(receivedMessage);
51  addMessageProperty(receivedMessage, hidden, YES);
52 
53  mWaitThread = new TTThread(NULL, NULL);
54 }
55 
56 WebSocket::~WebSocket()
57 {
58  TTObject webSocketProtocol(this);
59 
60  if (mAnswerManager)
61  delete mAnswerManager;
62 
63  if (mSenderManager)
64  delete mSenderManager;
65 
66  if (mWebSocketReceive.valid()) {
67 
68  mWebSocketReceive.unregisterObserverForNotifications(webSocketProtocol);
69  mWebSocketReceive = TTObject();
70  }
71 
72  if (mWaitThread)
73  mWaitThread->wait();
74 
75  delete mWaitThread;
76 }
77 
78 TTErr WebSocket::Scan(const TTValue& inputValue, TTValue& outputValue)
79 {
80  return kTTErrNone;
81 }
82 
83 /*!
84  * Run the reception thread
85  *
86  */
87 TTErr WebSocket::Run(const TTValue& inputValue, TTValue& outputValue)
88 {
89  TTValue v, out;
90  TTUInt16 port;
91  TTSymbol htmlPath;
92  TTErr err;
93 
94  if (inputValue.size())
95  return kTTErrGeneric;
96 
97  if (!mRunning) {
98 
99  TTObject webSocketProtocol(this);
100 
101  // select local application to get its port parameter
103  webSocketProtocol.get("port", v);
104 
105  // if the local application already setup its port
106  if (v.size()) {
107 
108  port = v[0];
109 
110  // create all internal objects
111  mAnswerManager = new WebSocketAnswerManager((WebSocketPtr)this);
112  mSenderManager = new WebSocketSenderManager();
113 
114  mWebSocketReceive = TTObject("web.receive");
115 
116  if (mWebSocketReceive.valid()) {
117 
118  err = mWebSocketReceive.set("port", v);
119 
120  if (!err) {
121 
122  // select local application to get its HtmlPath parameter
123  v.clear();
124  webSocketProtocol.get("htmlPath", v);
125 
126  if (v.size())
127  htmlPath = v[0];
128 
129  if (htmlPath == "")
130  v = WEBSOCKET_DEFAULT_HTML_PATH;
131 
132  mWebSocketReceive.set("htmlPath", v);
133 
134  mWebSocketReceive.registerObserverForNotifications(webSocketProtocol); // using our 'receivedMessage' method
135 
136  // wait to avoid strange crash when run and stop are called to quickly
137  mWaitThread->sleep(1);
138 
139  mRunning = YES;
140 
141  // Create a distant application called "telecommande" and register it
142  err = mApplicationManager.send("ApplicationInstantiateDistant", WEBSOCKET_DEFAULT_REMOTE_APPNAME, out);
143  if (!err) {
144  ApplicationRegister(WEBSOCKET_DEFAULT_REMOTE_APPNAME, out);
145 
146  return kTTErrNone;
147  }
148  else
149  TTLogError("unable to create %s distant application \n", WEBSOCKET_DEFAULT_REMOTE_APPNAME);
150  }
151  else
152  TTLogError("unable to bind to port %ld", port);
153  }
154  else
155  TTLogError("unable to create a websocket receiver");
156  }
157  }
158 
159  return kTTErrGeneric;
160 }
161 
162 /*!
163  * Stop the reception thread
164  *
165  */
166 TTErr WebSocket::Stop(const TTValue& inputValue, TTValue& outputValue)
167 {
168  // WebSocket doesn't need a thread per application
169  if (inputValue.size())
170  return kTTErrGeneric;
171 
172  if (mRunning) {
173 
174  TTObject webSocketProtocol(this);
175 
176  if (mAnswerManager)
177  delete mAnswerManager;
178 
179  if (mSenderManager)
180  delete mSenderManager;
181 
182  if (mWebSocketReceive.valid()) {
183 
184  mWebSocketReceive.unregisterObserverForNotifications(webSocketProtocol);
185  mWebSocketReceive = TTObject();
186  }
187 
188  // wait to avoid strange crash when run and stop are called to quickly
189  mWaitThread->sleep(1);
190 
191  mRunning = NO;
192 
193  return kTTErrNone;
194  }
195 
196  return kTTErrGeneric;
197 }
198 
199 /**************************************************************************************************************************
200  *
201  * SEND REQUEST METHODS
202  *
203  **************************************************************************************************************************/
204 
205 /*!
206  * Send a discover request to an application to get a part of the namespace at the given address
207  *
208  * \param to : the application where to discover
209  * \param address : the address to discover
210  * \param returnedType : the type of the node at the address (default is none which means no type)
211  * \param returnedChildren : all names of nodes below the address
212  * \param returnedAttributes : all attributes the node at the address
213  * \return errorcode : kTTErrNone means the answer has been received, kTTErrValueNotFound means something is bad in the request
214  else it returns kTTErrGeneric if no answer or timeout
215  */
216 TTErr WebSocket::SendDiscoverRequest(TTSymbol to, TTAddress address,
217  TTSymbol& returnedType,
218  TTValue& returnedChildren,
219  TTValue& returnedAttributes,
220  TTUInt8 tryCount)
221 {
222 // TTValue answer;
223 // TTString localAppName, operation;
224 // TTInt32 state;
225 // JSONNode* jsonNode;
226 //
227 // // create JSonNode
228 // jsonNode = new JSONNode(JSON_NODE);
229 //
230 // // edit local app name and operation
231 // localAppName = mLocalApplicationName.c_str();
232 // operation = WEBSOCKET_REQUEST_DISCOVER;
233 //
234 // jsonNode->push_back(JSONNode(WEBSOCKET_JSON_SENDER, localAppName));
235 // jsonNode->push_back(JSONNode(WEBSOCKET_JSON_OPERATION, operation));
236 // jsonNode->push_back(JSONNode(address.c_str(), NULL));
237 //
238 // if (!sendMessage(to, TTSymbol(localAppName), TTSymbol(operation), jsonNode)) {
239 //
240 //#ifdef TT_PROTOCOL_DEBUG
241 // std::cout << "WebSocket : applicationSendDiscoverRequest " << std::endl;
242 //#endif
243 //
244 // // Wait for an answer
245 // mAnswerManager->AddDiscoverAnswer(to, address);
246 //
247 // state = NO_ANSWER;
248 // do
249 // {
250 // state = mAnswerManager->CheckDiscoverAnswer(to, address, answer);
251 // }
252 // while (state == NO_ANSWER);
253 //
254 // if (state == ANSWER_RECEIVED)
255 // return mAnswerManager->ParseDiscoverAnswer(answer, returnedType, returnedChildren, returnedAttributes);
256 //
257 // else if (state == TIMEOUT_EXCEEDED && tryCount < MAX_TRY)
258 // return SendDiscoverRequest(to, address, returnedType, returnedChildren, returnedAttributes, tryCount+1);
259 // }
260 
261  return kTTErrGeneric;
262 }
263 
264 /*!
265  * Send a discover all request to an application to fill all the directory under this address
266  *
267  * \param to : the application where to discover
268  * \param address : the address to discover
269  * \param node : the node for this address
270  * \param tryCount : number of try for this request
271  * \return errorcode : kTTErrNone means the answer has been received, kTTErrValueNotFound means something is bad in the request
272  else it returns kTTErrGeneric if no answer or timeout
273  */
274 TTErr WebSocket::SendDiscoverAllRequest(TTSymbol to, TTAddress address,
275  TTNodePtr node,
276  TTUInt8 tryCount)
277 {
278 // TTValue answer;
279 // TTString localAppName, operation;
280 // TTInt32 state;
281 // JSONNode* jsonNode;
282 //
283 // // create JSonNode
284 // jsonNode = new JSONNode(JSON_NODE);
285 //
286 // // edit local app name and operation
287 // localAppName = mLocalApplicationName.c_str();
288 // operation = WEBSOCKET_REQUEST_DISCOVER_ALL;
289 //
290 // jsonNode->push_back(JSONNode(WEBSOCKET_JSON_SENDER, localAppName));
291 // jsonNode->push_back(JSONNode(WEBSOCKET_JSON_OPERATION, operation));
292 // jsonNode->push_back(JSONNode(address.c_str(), NULL));
293 //
294 // if (!sendMessage(to, TTSymbol(localAppName), TTSymbol(operation), jsonNode)) {
295 //
296 //#ifdef TT_PROTOCOL_DEBUG
297 // std::cout << "WebSocket : applicationSendDiscoverRequest " << std::endl;
298 //#endif
299 //
300 // // Wait for an answer
301 // mAnswerManager->AddDiscoverAllAnswer(to, address);
302 //
303 // state = NO_ANSWER;
304 // do
305 // {
306 // state = mAnswerManager->CheckDiscoverAllAnswer(to, address, answer);
307 // }
308 // while (state == NO_ANSWER);
309 //
310 // if (state == ANSWER_RECEIVED)
311 // return mAnswerManager->ParseDiscoverAllAnswer(answer, node);
312 //
313 // else if (state == TIMEOUT_EXCEEDED && tryCount < MAX_TRY)
314 // return SendDiscoverAllRequest(to, address, node, tryCount+1);
315 // }
316 
317  return kTTErrGeneric;
318 }
319 
320 /*!
321  * Send a get request to an application to get a value at the given address
322  *
323  * \param to : the application where to get
324  * \param address : the address to get
325  * \param returnedValue : the value which is going to be filled
326  * \return errorcode : kTTErrNone means the answer has been received, kTTErrValueNotFound means something is bad in the request
327  else it returns kTTErrGeneric if no answer or timeout
328  */
329 TTErr WebSocket::SendGetRequest(TTSymbol to, TTAddress address,
330  TTValue& returnedValue,
331  TTUInt8 tryCount)
332 {
333 // TTValue v, arguments;
334 // TTString localAppName, operation;
335 // TTInt32 state;
336 // JSONNode* jsonNode;
337 //
338 // // create JSonNode
339 // jsonNode = new JSONNode(JSON_NODE);
340 //
341 // // edit local app name and operation
342 // localAppName = mLocalApplicationName.c_str();
343 // operation = WEBSOCKET_REQUEST_GET;
344 //
345 // jsonNode->push_back(JSONNode(WEBSOCKET_JSON_SENDER, localAppName));
346 // jsonNode->push_back(JSONNode(WEBSOCKET_JSON_OPERATION, operation));
347 // jsonNode->push_back(JSONNode(address.c_str(), NULL));
348 //
349 // // TODO : add possibility to get several attributes
350 // if (!sendMessage(to, TTSymbol(localAppName), TTSymbol(operation), jsonNode)) {
351 //
352 //#ifdef TT_PROTOCOL_DEBUG
353 // std::cout << "WebSocket : applicationSendGetRequest " << std::endl;
354 //#endif
355 //
356 // // Wait for an answer
357 // mAnswerManager->AddGetAnswer(to, address);
358 //
359 // state = ANSWER_RECEIVED;
360 // do
361 // {
362 // state = mAnswerManager->CheckGetAnswer(to, address, returnedValue);
363 // }
364 // while(state == NO_ANSWER);
365 //
366 // if (state == ANSWER_RECEIVED)
367 // return kTTErrNone;
368 //
369 // else if (state == TIMEOUT_EXCEEDED && tryCount < MAX_TRY)
370 // return SendGetRequest(to, address, returnedValue, tryCount+1);
371 //
372 // }
373 
374  return kTTErrGeneric;
375 }
376 
377 /*!
378  * Send a set request to set a value of a specific application
379  *
380  * \param to : the application where to set
381  * \param address : the address to set
382  * \param value : anything to send
383  * \return errorcode : kTTErrNone means the answer has been received, kTTErrValueNotFound means something is bad in the request
384  */
385 TTErr WebSocket::SendSetRequest(TTSymbol to, TTAddress address,
386  TTValue& value,
387  TTUInt8 tryCount)
388 {
389 // TTValue arguments;
390 // TTString localAppName, operation;
391 // JSONNode* jsonNode;
392 //
393 // // create JSonNode
394 // jsonNode = new JSONNode(JSON_NODE);
395 //
396 // // edit local app name and operation
397 // localAppName = mLocalApplicationName.c_str();
398 // operation = WEBSOCKET_REQUEST_SET;
399 //
400 // jsonNode->push_back(JSONNode(WEBSOCKET_JSON_SENDER, localAppName));
401 // jsonNode->push_back(JSONNode(WEBSOCKET_JSON_OPERATION, operation));
402 // addChildToJson(jsonNode, address.c_str(), value);
403 //
404 //#ifdef TT_PROTOCOL_DEBUG
405 // std::cout << "WebSocket : applicationSendSetRequest " << std::endl;
406 //#endif
407 //
408 // // TODO : add possibility to set several attributes
409 // return sendMessage(to, TTSymbol(localAppName), TTSymbol(operation), jsonNode);
410 
411  return kTTErrGeneric;
412 }
413 
414 /*!
415  * Send a listen request to a specific application
416  *
417  * \param to : the application where to listen
418  * \param address : the address to listen
419  * \param enable : enable/disable the listening
420  * \return errorcode : kTTErrNone means the answer has been received, kTTErrValueNotFound means something is bad in the request
421  */
422 TTErr WebSocket::SendListenRequest(TTSymbol to, TTAddress address,
423  TTBoolean enable,
424  TTUInt8 tryCount)
425 {
426 // TTValue arguments;
427 // TTString localAppName, operation;
428 // JSONNode* jsonNode;
429 //
430 // // create JSonNode
431 // jsonNode = new JSONNode(JSON_NODE);
432 //
433 // // edit local app name and operation
434 // localAppName = mLocalApplicationName.c_str();
435 // operation = WEBSOCKET_REQUEST_LISTEN;
436 //
437 // jsonNode->push_back(JSONNode(WEBSOCKET_JSON_SENDER, localAppName));
438 // jsonNode->push_back(JSONNode(WEBSOCKET_JSON_OPERATION, operation));
439 // jsonNode->push_back(JSONNode(address.c_str(), enable));
440 //
441 //#ifdef TT_PROTOCOL_DEBUG
442 // std::cout << "WebSocket : applicationSendListenRequest " << std::endl;
443 //#endif
444 //
445 // return sendMessage(to, TTSymbol(localAppName), TTSymbol(operation), jsonNode);
446 
447  return kTTErrGeneric;
448 }
449 
450 
451 /**************************************************************************************************************************
452  *
453  * SEND ANSWER METHODS
454  *
455  **************************************************************************************************************************/
456 
457 /*!
458  * Send a disover answer to a application which ask for.
459  *
460  * \param to : the application where to send answer
461  * \param address : the address where comes from the description
462  * \param returnedType : the type of the node at the address (default is none which means no type)
463  * \param returnedChildren : all names of nodes below the address
464  * \param returnedAttributes : all attributes the node at the address
465  */
466 TTErr WebSocket::SendDiscoverAnswer(TTSymbol to, TTAddress address,
467  TTSymbol& returnedType,
468  TTValue& returnedChildren,
469  TTValue& returnedAttributes,
470  TTErr err)
471 {
472  TTString localAppName, operation;
473  TTString attributesString, childrenString;
474  TTSymbol symValue;
475  JSONNode *jsonNode, *childNode;
476 
477  // create JSonNode
478  jsonNode = new JSONNode(JSON_NODE);
479 
480  // edit local app name and operation
481  if (!err) {
482  localAppName = mLocalApplicationName.c_str();
483  operation = WEBSOCKET_ANSWER_DISCOVER;
484  childNode = new JSONNode(JSON_NODE);
485 
486  // edit arguments merging all returned fields
487  // note : here we need to begin by the end
488  // and then prepend fields one by one
489  if (returnedAttributes.size()) {
490 
491  for (TTUInt32 i = 0; i < returnedAttributes.size(); ++i) {
492  symValue = returnedAttributes[i];
493  attributesString += symValue;
494  if (i < returnedAttributes.size()-1)
495  attributesString += " ";
496  }
497 
498  childNode->push_back(JSONNode(WEBSOCKET_JSON_ATTRIBUTES, attributesString));
499  }
500 
501  if (returnedChildren.size()) {
502  for (TTUInt32 i = 0; i < returnedChildren.size(); ++i) {
503  symValue = returnedChildren[i];
504  childrenString += symValue;
505  if (i < returnedChildren.size()-1)
506  childrenString += " ";
507  }
508 
509  childNode->push_back(JSONNode(WEBSOCKET_JSON_CHILDREN, childrenString));
510  }
511 
512  childNode->push_back(JSONNode(WEBSOCKET_JSON_TYPE, returnedType.c_str()));
513 
514  childNode->set_name(address.removeAttribute().c_str());
515  }
516  else {
517  localAppName = mLocalApplicationName.c_str();
518  operation = WEBSOCKET_ERROR_DISCOVER;
519 
520  // TODO : send explicit error description
521  childNode = new JSONNode(WEBSOCKET_JSON_ERROR, err);
522  }
523 
524  // append sender & operation to json
525  jsonNode->push_back(JSONNode(WEBSOCKET_JSON_SENDER, localAppName));
526  jsonNode->push_back(JSONNode(WEBSOCKET_JSON_OPERATION, operation));
527  jsonNode->push_back(*childNode);
528 
529 #ifdef TT_PROTOCOL_DEBUG
530  std::cout << "WebSocket : applicationSendDiscoverAnswer " << std::endl;
531 #endif
532 
533  return sendMessage(to, TTSymbol(localAppName), TTSymbol(operation), jsonNode);
534 }
535 
536 /*!
537  * Send a discover all answer to a application which ask for.
538  *
539  * \param to : the application where to send answer
540  * \param address : the address where comes from the description
541  * \param node : the node for this address
542  */
543 TTErr WebSocket::SendDiscoverAllAnswer(TTSymbol to, TTAddress address,
544  TTNodePtr node,
545  TTErr err)
546 {
547  TTString localAppName, operation;
548  TTString attributesString, childrenString;
549  TTSymbol symValue;
550  JSONNode *jsonNode, *childNode;
551 
552  // create JSonNode
553  jsonNode = new JSONNode(JSON_NODE);
554  childNode = new JSONNode(JSON_NODE);
555 
556  // edit local app name and operation
557  if (!err) {
558  localAppName = mLocalApplicationName.c_str();
559  operation = WEBSOCKET_ANSWER_DISCOVER_ALL;
560 
561  // go throught node tree and build json string of the whole namespace under address
562  parseChildren(childNode, node, true);
563  }
564  else {
565  localAppName = mLocalApplicationName.c_str();
566  operation = WEBSOCKET_ERROR_DISCOVER_ALL;
567 
568  // TODO : send explicit error description
569  childNode = new JSONNode(WEBSOCKET_JSON_ERROR, err);
570  }
571 
572  // append sender & operation to json
573  jsonNode->push_back(JSONNode(WEBSOCKET_JSON_SENDER, localAppName));
574  jsonNode->push_back(JSONNode(WEBSOCKET_JSON_OPERATION, operation));
575  jsonNode->push_back(*childNode);
576 
577 #ifdef TT_PROTOCOL_DEBUG
578  std::cout << "WebSocket : applicationSendDiscoverAllAnswer " << std::endl;
579 #endif
580 
581  return sendMessage(to, TTSymbol(localAppName), TTSymbol(operation), jsonNode);
582 }
583 
584 /*!
585  * Send a get answer to an application which ask for.
586  *
587  * \param to : the application where to send answer
588  * \param address : the address where comes from the value
589  * \param returnedValue : the value of the attribute at the address
590  */
591 TTErr WebSocket::SendGetAnswer(TTSymbol to, TTAddress address,
592  const TTValue& returnedValue,
593  TTErr err)
594 {
595  TTString localAppName, operation;
596  JSONNode *jsonNode, *childNode;
597 
598  // create JSonNode
599  jsonNode = new JSONNode(JSON_NODE);
600 
601  // edit local app name and operation
602  if (!err) {
603  localAppName = mLocalApplicationName.c_str();
604  operation = WEBSOCKET_ANSWER_GET;
605 
606  jsonNode->push_back(JSONNode(WEBSOCKET_JSON_SENDER, localAppName));
607  jsonNode->push_back(JSONNode(WEBSOCKET_JSON_OPERATION, operation));
608 
609  childNode = new JSONNode(JSON_NODE);
610  childNode->set_name(address.removeAttribute().c_str());
611 
612  // if only value attribute to get, add it like this in json : <address> : <value>
613  if (address.getAttribute() == kTTSym_value)
614  {
615  addChildToJson(jsonNode, address.removeAttribute(), returnedValue);
616  }
617  // else like this : <address> : { ":type" : <type> }
618  // TODO : do it for several attributes like this : <address> : { ":type" : <type>, ":value" : <value>, ":range" : <range> }
619  else
620  {
621  childNode = new JSONNode(JSON_NODE);
622  childNode->set_name(address.removeAttribute().c_str());
623  addChildToJson(childNode, address.getAttribute(), returnedValue);
624  jsonNode->push_back(*childNode);
625  }
626  }
627  else {
628  localAppName = mLocalApplicationName.c_str();
629  operation = WEBSOCKET_ERROR_GET;
630 
631  jsonNode->push_back(JSONNode(WEBSOCKET_JSON_SENDER, localAppName));
632  jsonNode->push_back(JSONNode(WEBSOCKET_JSON_OPERATION, operation));
633 
634  // TODO : send explicit error description
635  childNode = new JSONNode(WEBSOCKET_JSON_ERROR, err);
636  jsonNode->push_back(*childNode);
637  }
638 
639 #ifdef TT_PROTOCOL_DEBUG
640  std::cout << "WebSocket : applicationSendGetAnswer" << std::endl;
641 #endif
642 
643  return sendMessage(to, TTSymbol(localAppName), TTSymbol(operation), jsonNode);
644 }
645 
646 /*!
647  * Send a listen answer to an application which ask for.
648  *
649  * \param to : the application where to send answer
650  * \param address : the address where comes from the value
651  * \param returnedValue : the value of the attribute at the address
652  */
653 TTErr WebSocket::SendListenAnswer(TTSymbol to, TTAddress address,
654  const TTValue& returnedValue,
655  TTErr err)
656 {
657  TTString localAppName, operation;
658  JSONNode *jsonNode, *childNode;
659 
660  // create JSonNode
661  jsonNode = new JSONNode(JSON_NODE);
662 
663  // edit local app name and operation
664  if (!err) {
665  localAppName = mLocalApplicationName.c_str();
666  operation = WEBSOCKET_ANSWER_LISTEN;
667 
668  childNode = new JSONNode(address.removeAttribute().c_str(), true);
669  }
670  else {
671  localAppName = mLocalApplicationName.c_str();
672  operation = WEBSOCKET_ERROR_LISTEN;
673 
674  // TODO : send explicit error description
675  childNode = new JSONNode(WEBSOCKET_JSON_ERROR, err);
676  }
677 
678  // append sender & operation to json
679  jsonNode->push_back(JSONNode(WEBSOCKET_JSON_SENDER, localAppName));
680  jsonNode->push_back(JSONNode(WEBSOCKET_JSON_OPERATION, operation));
681  jsonNode->push_back(*childNode);
682 
683 #ifdef TT_PROTOCOL_DEBUG
684  std::cout << "WebSocket : applicationSendListenAnswer " << std::endl;
685 #endif
686 
687  return sendMessage(to, TTSymbol(localAppName), TTSymbol(operation), jsonNode);
688 }
689 
690 TTErr WebSocket::sendMessage(TTSymbol distantApplicationName, TTSymbol localApplicationName, TTSymbol operation, JSONNode* jsonNode)
691 {
692  TTHashPtr parameters = NULL;
693  TTObject aWebSender;
694  TTString header;
695  TTValue v, message, none;
696  TTErr err;
697 
698  if (!mSenderManager)
699  return kTTErrGeneric;
700 
701  // Check the application registration
702  err = mApplicationParameters.lookup(distantApplicationName, v);
703 
704  if (!err) {
705 
706  aWebSender = mSenderManager->lookup(distantApplicationName);
707 
708  if (aWebSender.valid()) {
709  // get json string
710  json_string js = jsonNode->write_formatted();
711 
712 #ifdef TT_PROTOCOL_DEBUG
713  std::cout << "json formatted to send : " << js << std::endl;
714 #endif
715 
716  // add it to message TTValue
717  message = TTSymbol(js);
718 
719  // send message
720  err = aWebSender.send(TTSymbol("send"), message, none);
721 
722  if (!err && mActivity) {
723  header = mLocalApplicationName.c_str();
724  header += operation;
725 
726  v = header;
728  }
729  }
730  }
731 
732  return err;
733 }
734 
735 TTErr WebSocket::parseJSON(const JSONNode &n, TTString address, TTValue& value)
736 {
737  if (n == NULL)
738  {
739  return kTTErrGeneric;
740  }
741 
742  JSONNode::const_iterator i = n.begin();
743 
744  while (i != n.end())
745  {
746  /*if (*i == NULL)
747  {
748  return kTTErrGeneric;
749  }*/
750 
751  // get the node name
752  std::string node_name = i->name();
753 
754  if (node_name == WEBSOCKET_JSON_SENDER)
755  {
756  // add sender id as first arg
757  value = TTSymbol(i->as_string());
758  }
759  else if (node_name == WEBSOCKET_JSON_OPERATION)
760  {
761  // append command id as second arg
762  value.append(TTSymbol(i->as_string()));
763  }
764  else
765  {
766  // add first / only if not already specified
767  TTString nm = node_name;
768  if (nm.find_first_of('/') != 0)
769  address += "/";
770 
771  // append node name to osc-like address
772  address += node_name.c_str();
773 
774  // recursively call ourselves to dig deeper into the tree
775  if (i->type() == JSON_NODE)
776  {
777  parseJSON(*i, address, value);
778  }
779  else
780  {
781  // parsing finished, append built osc-like address and append value
782 
783  // append address as third arg
784  value.append(TTSymbol(address));
785 
786  // append value as fourth arg
787  if (i->type() == JSON_STRING)
788  {
789  value.append(TTSymbol(i->as_string()));
790  }
791  else if(i->type() == JSON_NUMBER)
792  {
793  value.append(i->as_float());
794  }
795  else if(i->type() == JSON_BOOL)
796  {
797  value.append(i->as_bool());
798  }
799  else if(i->type() == JSON_ARRAY)
800  {
801  value.append(i->as_array());
802  }
803  }
804  }
805 
806  ++i;
807  }
808  return kTTErrNone;
809 }
810 
811 
812 TTErr WebSocket::receivedMessage(const TTValue& message, TTValue& outputValue)
813 {
814  TTSymbol sender, operation;
815  TTString headerString, address, aString;
816  TTInt32 operationStart;
817  TTValue arguments;
818  TTAddress whereTo = kTTAdrsEmpty;
819  TTValue v, jsonContent;
820  TTBoolean listenEnabled;
821  TTErr err;
822 
823  if (mActivity) ActivityInMessage(message);
824 
825  aString = message.toString();
826 
827 #ifdef TT_PROTOCOL_DEBUG
828  cout << endl;
829  cout << "Message received : " << aString.c_str() << endl;
830 #endif
831 
832  // parse json string
833  JSONNode rootNode = libjson::parse(aString.c_str());
834  json_string js = rootNode.write_formatted();
835 
836  parseJSON(rootNode, address, jsonContent);
837 
838  // get sender
839  sender = jsonContent[0];
840  if (!sender)
841  return kTTErrGeneric;
842 
843  // get operation
844  operation = jsonContent[1];
845  TTString operationString = operation;
846  if (!operation)
847  return kTTErrGeneric;
848 
849  // get address
850  whereTo = jsonContent[2];
851 
852  if (operation == TTSymbol(WEBSOCKET_REQUEST_SET))
853  {
854 #ifdef TT_PROTOCOL_DEBUG
855  cout << "Receive set request at " << whereTo.c_str() << endl;
856 #endif
857 
858  // get value
859  // TODO : make a loop to set several attributes
860  arguments = jsonContent[3];
861  return ReceiveSetRequest(kTTSymEmpty, whereTo, arguments);
862  }
863  else
864  {
865  // Is it a request ?
866  operationStart = operationString.find_first_of('?');
867  if (operationStart >= 0)
868  {
869  // Check the sender application registration
870  err = mApplicationParameters.lookup(sender, v);
871  if (!err)
872  {
873 #ifdef TT_PROTOCOL_DEBUG
874  cout << "Receive " << operation.c_str() << " request from "<< sender.c_str() << " at " << whereTo.c_str() << endl;
875 #endif
876 
877  // switch on request
878  if (operation == TTSymbol(WEBSOCKET_REQUEST_DISCOVER))
879  return ReceiveDiscoverRequest(sender, whereTo);
880 
881  else if (operation == TTSymbol(WEBSOCKET_REQUEST_DISCOVER_ALL))
882  return ReceiveDiscoverAllRequest(sender, whereTo);
883 
884  else if (operation == TTSymbol(WEBSOCKET_REQUEST_GET))
885  return ReceiveGetRequest(sender, whereTo);
886 
887  else if (operation == TTSymbol(WEBSOCKET_REQUEST_LISTEN))
888  {
889  // parse enable/disable
890  listenEnabled = jsonContent[3];
891  return ReceiveListenRequest(sender, whereTo, listenEnabled);
892  }
893 
894  else
895  ; // TODO send bad request error notification
896  }
897 
898  }// end if starts by '?'
899 
900  // Is it a answer :
901  operationStart = operationString.find_first_of(':');
902  if (operationStart >= 0)
903  {
904  // Check the sender application registration
905  err = mApplicationParameters.lookup(sender, v);
906  if (!err) {
907 
908 #ifdef TT_PROTOCOL_DEBUG
909  cout << "Receive " << operation.c_str() << " answer from "<< sender.c_str() << " at " << whereTo.c_str() << endl;
910 #endif
911 
912  // switch on answer
913  if (operation == TTSymbol(WEBSOCKET_ANSWER_DISCOVER))
914  return mAnswerManager->ReceiveDiscoverAnswer(sender, whereTo, arguments);
915 
916  else if (operation == TTSymbol(WEBSOCKET_ANSWER_DISCOVER_ALL))
917  return mAnswerManager->ReceiveDiscoverAllAnswer(sender, whereTo, arguments);
918 
919  else if (operation == TTSymbol(WEBSOCKET_ANSWER_GET))
920  return mAnswerManager->ReceiveGetAnswer(sender, whereTo, arguments);
921 
922  else if (operation == TTSymbol(WEBSOCKET_ANSWER_LISTEN))
923  return ReceiveListenAnswer(sender, whereTo, arguments);
924 
925  }
926  } // end if starts by ':'
927 
928  // Is it an error :
929  operationStart = operationString.find_first_of('!');
930  if (operationStart >= 0)
931  {
932  // Check the sender application registration
933  err = mApplicationParameters.lookup(sender, v);
934  if (!err) {
935 
936 #ifdef TT_PROTOCOL_DEBUG
937  cout << "Receive " << operation.c_str() << " error from "<< sender.c_str() << " at " << whereTo.c_str() << endl;
938 #endif
939 
940  // switch on answer
941  if (operation == TTSymbol(WEBSOCKET_ERROR_DISCOVER))
942  return mAnswerManager->ReceiveDiscoverAnswer(sender, whereTo, arguments, kTTErrGeneric);
943 
944  else if (operation == TTSymbol(WEBSOCKET_ERROR_DISCOVER_ALL))
945  return mAnswerManager->ReceiveDiscoverAllAnswer(sender, whereTo, arguments, kTTErrGeneric);
946 
947  else if (operation == TTSymbol(WEBSOCKET_ERROR_GET))
948  return mAnswerManager->ReceiveGetAnswer(sender, whereTo, arguments, kTTErrGeneric);
949 
950  // théo - is there error for listen request ?
951  }
952  } // end if starts by '!'
953 
954  } // end else
955 
956  return kTTErrNone;
957 }
958 
959 
960 void addChildToJson(JSONNode* jsonNode, TTSymbol childName, TTValue value)
961 {
962  TTString s;
963  TTSymbol addressValue;
964  TTSymbol symValue;
965  TTString stringValue;
966  TTInt32 intValue;
967  TTFloat64 floatValue;
968  TTDataType valueType;
969  TTBoolean boolValue;
970 
971  // check value type and add it in json tree
972  valueType = value[0].type();
973 
974  if (childName == TTSymbol("rangeBounds"))
975  {
976  valueType = kTypeSymbol;
977  value.toString();
978  value.get(0, stringValue);
979  }
980 
981  if (valueType == kTypeSymbol && childName == TTSymbol("rangeBounds")) {
982  // std::cout << childName.c_str() << " " << stringValue.data() << std::endl;
983  jsonNode->push_back(JSONNode(childName.c_str(), stringValue.data()));
984  }
985  else if (valueType == kTypeSymbol && childName != TTSymbol("rangeBounds")) {
986  symValue = value[0];
987  // std::cout << childName.c_str() << " " << symValue.c_str() << std::endl;
988  jsonNode->push_back(JSONNode(childName.c_str(), symValue.c_str()));
989  }
990  else if (valueType == kTypeBoolean) {
991  boolValue = value[0];
992  // std::cout << childName.c_str() << " " << boolValue << std::endl;
993  jsonNode->push_back(JSONNode(childName.c_str(), boolValue));
994  }
995  else if (valueType == kTypeUInt8 || valueType == kTypeUInt16 || valueType == kTypeUInt32 || valueType == kTypeUInt64) {
996  intValue = value[0];
997  // std::cout << childName.c_str() << " " << intValue << std::endl;
998  jsonNode->push_back(JSONNode(childName.c_str(), intValue));
999  }
1000  else if (valueType == kTypeInt8 || valueType == kTypeInt16 || valueType == kTypeInt32 || valueType == kTypeInt64) {
1001  intValue = value[0];
1002  // std::cout << childName.c_str() << " " << intValue << std::endl;
1003  jsonNode->push_back(JSONNode(childName.c_str(), intValue));
1004  }
1005  else if (valueType == kTypeFloat32 || valueType == kTypeFloat64) {
1006  floatValue = value[0];
1007  // std::cout << childName.c_str() << " " << floatValue << std::endl;
1008  jsonNode->push_back(JSONNode(childName.c_str(), (float)floatValue));
1009  }
1010 }
1011 
1012 void addAttributeToJson(TTObject param, JSONNode* jsonNode, TTSymbol attrName)
1013 {
1014  TTValue v;
1015  TTString s;
1016  TTSymbol addressValue;
1017  TTSymbol symValue;
1018  TTString stringValue;
1019  TTInt32 intValue;
1020  TTFloat64 floatValue;
1021  TTDataType valueType = kTypeNone;
1022  TTBoolean boolValue;
1023  TTErr err = kTTErrNone;
1024 
1025  // get the Value of an attribute
1026  v.clear();
1027  err = param.get(attrName, v);
1028 
1029  if (v.size() == 0)
1030  return;
1031 
1032  if (attrName == TTSymbol("rangeBounds"))
1033  {
1034  valueType = kTypeSymbol;
1035  v.toString();
1036  v.get(0, stringValue);
1037  }
1038  else
1039  valueType = v[0].type();
1040 
1041  // add this attribute in json tree
1042  if (valueType == kTypeSymbol && attrName == TTSymbol("rangeBounds")) {
1043  // std::cout << attrName.c_str() << " " << stringValue.data() << std::endl;
1044  jsonNode->push_back(JSONNode(attrName.c_str(), stringValue.data()));
1045  }
1046  else if (valueType == kTypeSymbol && attrName != TTSymbol("rangeBounds")) {
1047  symValue = v[0];
1048  // std::cout << attrName.c_str() << " " << symValue.c_str() << std::endl;
1049  jsonNode->push_back(JSONNode(attrName.c_str(), symValue.c_str()));
1050  }
1051  else if (valueType == kTypeBoolean) {
1052  boolValue = v[0];
1053  // std::cout << attrName.c_str() << " " << boolValue << std::endl;
1054  jsonNode->push_back(JSONNode(attrName.c_str(), boolValue));
1055  }
1056  else if (valueType == kTypeUInt8 || valueType == kTypeUInt16 || valueType == kTypeUInt32 || valueType == kTypeUInt64) {
1057  intValue = v[0];
1058  // std::cout << attrName.c_str() << " " << intValue << std::endl;
1059  jsonNode->push_back(JSONNode(attrName.c_str(), intValue));
1060  }
1061  else if (valueType == kTypeInt8 || valueType == kTypeInt16 || valueType == kTypeInt32 || valueType == kTypeInt64) {
1062  intValue = v[0];
1063  // std::cout << attrName.c_str() << " " << intValue << std::endl;
1064  jsonNode->push_back(JSONNode(attrName.c_str(), intValue));
1065  }
1066  else if (valueType == kTypeFloat32 || valueType == kTypeFloat64) {
1067  floatValue = v[0];
1068  // std::cout << attrName.c_str() << " " << floatValue << std::endl;
1069  jsonNode->push_back(JSONNode(attrName.c_str(), (float)floatValue));
1070  }
1071 }
1072 
1073 // method to extract a substring safely
1074 char *str_sub (const char *s, unsigned int start, unsigned int end)
1075 {
1076  char *new_s = NULL;
1077 
1078  if (s != NULL && start < end) {
1079  new_s = (char*) malloc (sizeof (*new_s) * (end - start + 2));
1080  if (new_s != NULL) {
1081  unsigned int i;
1082 
1083  for (i = start; i <= end; i++) {
1084  new_s[i-start] = s[i];
1085  }
1086  new_s[i-start] = '\0';
1087  }
1088  else {
1089  fprintf (stderr, "Memoire insuffisante\n");
1090  exit (EXIT_FAILURE);
1091  }
1092  }
1093  return new_s;
1094 }
1095 
1096 void parseChildren(JSONNode* jsonNode, TTNodePtr ttNode, TTBoolean isFirstParsing)
1097 {
1098  TTAddress OSCaddress;
1099  TTValue v, attributeNames;
1100  TTList childList;
1101  TTNodePtr p_node;
1102  TTString s;
1103 
1104  ttNode->getAddress(OSCaddress);
1105  ttNode->getChildren(S_WILDCARD, S_WILDCARD, childList);
1106 
1107  const char* address = OSCaddress.c_str();
1108  char* nodeName;
1109 
1110  JSONNode* childNode = new JSONNode(JSON_NODE);
1111 
1112 // if (strrchr(address, '.') == NULL)
1113 // {
1114  // get the substring representing the last node name
1115 
1116  // address is only the root ("/")
1117  if (strcmp(address, "/") == 0)
1118  {
1119  nodeName = (char*)address;
1120  }
1121  // address is only the first node ("/firstNode")
1122  else if (strlen(address) > 1 && strrchr(address, '/')-address == 0)
1123  {
1124  nodeName = (char*)address;
1125  }
1126  // address is not only the root ("/firstNode/secondNode/...")
1127  else if (strlen(address) > 1)
1128  {
1129  const char* c = strrchr(address, '/');
1130  int start = c-address+1;
1131  int end = strlen(address)-1;
1132  nodeName = str_sub(address, start, end);
1133  }
1134 
1135  if (isFirstParsing)
1136  {
1137  childNode = jsonNode;
1138  childNode->set_name(nodeName);
1139  }
1140 
1141  if (childList.isEmpty())
1142  {
1143  // get the Data object of the Node
1144  TTObject param = ttNode->getObject();
1145 
1146  if (param != NULL)
1147  {
1148  addAttributeToJson(param, childNode, TTSymbol("type"));
1149  addAttributeToJson(param, childNode, TTSymbol("service"));
1150  addAttributeToJson(param, childNode, TTSymbol("valueDefault"));
1151  addAttributeToJson(param, childNode, TTSymbol("rangeBounds"));
1152  addAttributeToJson(param, childNode, TTSymbol("valueStepsize"));
1153  addAttributeToJson(param, childNode, TTSymbol("description"));
1154  // addAttributeToJson(param, childNode, TTSymbol("dynamicInstances"));
1155  // addAttributeToJson(param, childNode, TTSymbol("instanceBounds"));
1156  // addAttributeToJson(param, childNode, kTTSym_priority);
1157  // addAttributeToJson(param, childNode, TTSymbol("readonly"));
1158  }
1159  }
1160 // }
1161 
1162  // repeat recursively for each child
1163  for (childList.begin(); childList.end(); childList.next()) {
1164  childList.current().get(0,(TTPtr*)&p_node);
1165  parseChildren(childNode, p_node, false);
1166  }
1167 
1168  // when json node is pushed_back, it is copied into json tree so has to be modified before
1169  if(!isFirstParsing)
1170  {
1171  childNode->set_name(nodeName);
1172  jsonNode->push_back(*childNode);
1173 
1174  childNode->clear();
1175  delete childNode;
1176  }
1177 }
bool TTBoolean
Boolean flag, same as Boolean on the Mac.
Definition: TTBase.h:167
std::uint16_t TTUInt16
16 bit unsigned integer
Definition: TTBase.h:176
TTObject mApplicationManager
the application manager of the Modular framework.
Definition: TTProtocol.h:63
#define TT_PROTOCOL_INITIALIZE
Declares all members needed by any protocol class.
Definition: TTProtocol.h:31
TTErr ReceiveListenRequest(TTSymbol from, TTAddress address, TTBoolean enable)
Definition: TTProtocol.cpp:369
TTErr send(const TTSymbol aName)
Send a message to this object with no arguments.
Definition: TTObject.cpp:135
TTString toString(TTBoolean quotes=YES) const
Return the content as a single string with spaces between elements.
Definition: TTValue.h:351
TTErr lookup(const TTSymbol key, TTValue &value)
Find the value for the given key.
Definition: TTHash.cpp:76
We build a directory of TTNodes, and you can request a pointer for any TTNode, or add an observer to ...
Definition: TTNode.h:59
8-bit unsigned integer, range is 0 through 255.
Definition: TTBase.h:274
It's the WebSocket protocol.
const char * c_str() const
Return a pointer to the internal C-string.
Definition: TTString.h:83
The TTAddress class is used to represent a string and efficiently pass and compare that string...
Definition: TTAddress.h:29
Create and use Jamoma object instances.
Definition: TTObject.h:29
64-bit unsigned integer, range is 0 through 18,446,744,073,709,551,615.
Definition: TTBase.h:280
TTObject & getObject()
Get the object binded by this node.
Definition: TTNode.cpp:468
size_type size() const noexcept
Return the number of elements.
TTDataType
TTBlue Data Types Enumeration of data types used through out TTBlue, including the TTValue class and ...
Definition: TTBase.h:269
TTErr ActivityOutMessage(const TTValue &message)
Definition: TTProtocol.cpp:419
TTErr unregisterObserverForNotifications(const TTObject &anObservingObject)
Unregister an observer for notifications.
Definition: TTObject.cpp:161
Base class for all first-class Jamoma objects.
Definition: TTObjectBase.h:109
Maintain a collection of TTValue objects indexed by TTSymbol pointers.
Definition: TTHash.h:36
Symbol type.
Definition: TTBase.h:282
double TTFloat64
64 bit floating point number
Definition: TTBase.h:188
TTErr ReceiveDiscoverRequest(TTSymbol from, TTAddress address)
Definition: TTProtocol.cpp:291
16-bit unsigned integer, range is 0 through 65,535.
Definition: TTBase.h:276
void append(const T &anElementValueToAppend)
Insert a single TTElement at the end.
Definition: TTValue.h:243
void * TTPtr
A generic pointer.
Definition: TTBase.h:248
Create a web socket plugin.
Definition: WebSocket.h:33
TTErr get(const TTSymbol aName, T &aReturnedValue) const
Get an attribute value for an object.
TTErr registerObserverForNotifications(const TTObject &anObservingObject)
Register an observer.
Definition: TTObject.cpp:155
16-bit signed integer, range is −32,768 through 32,767.
Definition: TTBase.h:275
TTSymbol mLocalApplicationName
cache local application name
Definition: TTProtocol.h:66
A Protocol interface.
64-bit floating point
Definition: TTBase.h:272
TTErr set(const TTSymbol aName, T aValue)
Set an attribute value for an object.
The TTSymbol class is used to represent a string and efficiently pass and compare that string...
Definition: TTSymbol.h:26
void TTFOUNDATION_EXPORT TTLogError(TTImmutableCString message,...)
Platform and host independent method for posting errors.
Definition: TTBase.cpp:572
void get(const TTUInt16 index, T &returnedElementValue) const
DEPRECATED.
Definition: TTValue.h:591
Boolean (1/0) or (true/false) flag.
Definition: TTBase.h:281
TTBoolean mActivity
ATTRIBUTE : is the Protocol activity thread enable ?
Definition: TTProtocol.h:85
TTSymbol & getAttribute()
Get the attribute part.
Definition: TTAddress.h:130
#define addMessageWithArguments(name)
A convenience macro to be used by subclasses for registering messages.
Definition: TTMessage.h:27
TTErr ApplicationRegister(const TTValue &inputValue, TTValue &outputValue)
Register an application as a client of the protocol.
Definition: TTProtocol.cpp:97
TTErr ApplicationSelectLocal()
Select the local application to access to its own parameters value.
Definition: TTProtocol.cpp:233
const char * c_str() const
Return a pointer to the internal string as a C-string.
Definition: TTSymbol.h:77
std::int32_t TTInt32
32 bit signed integer
Definition: TTBase.h:177
64-bit signed integer, ragne is −9,223,372,036,854,775,808 through 9,223,372,036,854,775,807
Definition: TTBase.h:279
32-bit floating point
Definition: TTBase.h:271
void clear()
Clear all values from the vector, leaving with size of 0.
Definition: TTValue.h:131
TTErr ReceiveListenAnswer(TTSymbol from, TTAddress address, const TTValue &newValue)
Definition: TTProtocol.cpp:391
TTErr getAddress(TTAddress &returnedAddress, TTAddress from=kTTAdrsEmpty)
Get the address of the node.
Definition: TTNode.cpp:478
TTHash mApplicationParameters
ATTRIBUTE : hash table containing hash table of parameters for each application registered for commun...
Definition: TTProtocol.h:71
32-bit signed integer, range is -2,147,483,648 through 2,147,483,647.
Definition: TTBase.h:277
Something went wrong, but what exactly is not known. Typically used for context-specific problems...
Definition: TTBase.h:344
TTErr
Jamoma Error Codes Enumeration of error codes that might be returned by any of the TTBlue functions a...
Definition: TTBase.h:342
std::uint32_t TTUInt32
32 bit unsigned integer
Definition: TTBase.h:178
TTBoolean mRunning
ATTRIBUTE : is the Protocol reception thread enable ?
Definition: TTProtocol.h:84
TTErr ReceiveDiscoverAllRequest(TTSymbol from, TTAddress address)
Definition: TTProtocol.cpp:315
size_t find_first_of(const char aChar, size_t from=0)
Return the index of the first instance of a specified char in the string.
Definition: TTString.h:296
TTErr getChildren(TTSymbol &name, TTSymbol &instance, TTList &returnedChildren)
Get a linklist of children of the node : select them by name and instance (use wilcards to select the...
Definition: TTNode.cpp:301
TTErr ActivityInMessage(const TTValue &message)
Definition: TTProtocol.cpp:412
32-bit unsigned integer, range is 0 through 4,294,967,295.
Definition: TTBase.h:278
8-bit signed integer, range is -128 through 127.
Definition: TTBase.h:273
No Error.
Definition: TTBase.h:343
TTErr ReceiveGetRequest(TTSymbol from, TTAddress address)
Definition: TTProtocol.cpp:335
#define TT_PROTOCOL_CONSTRUCTOR
Declares instantiation and registration methods to add the protocol class as any TTObject class...
Definition: TTProtocol.h:23
#define addAttributeAsProtocolParameter(name, type)
Add a protocol parameter as an attribute of the class.
Definition: TTProtocol.h:48
TTAddress removeAttribute()
Return a new TTAddress without attribute part.
Definition: TTAddress.h:155
The TTString class is used to represent a string.
Definition: TTString.h:34
TTErr ReceiveSetRequest(TTSymbol from, TTAddress address, const TTValue &newValue)
Definition: TTProtocol.cpp:352
TTBoolean valid() const
Determine if the object contained by this TTObject is truly ready for use.
Definition: TTObject.cpp:179
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
unsigned char TTUInt8
8 bit unsigned integer (char)
Definition: TTBase.h:174
#define addMessageProperty(messageName, propertyName, initialValue)
A convenience macro to be used for registering properties of messages.
Definition: TTMessage.h:37