Main /
PtolemyIIAccessorTutorialWriting your first accessor using Ptolemy II has a number of different steps. Install Ptolemy IISee Ptolemy II for how to install Ptolemy II from the svn devel tree. Ptolemy II 10.0 is not up to date, use the svn devel tree instead. The TerraSwarm Accessors repositoryThe accessors are online at https://ptolemy.berkeley.edu/accessors. If Ptolemy II is built from the Ptolemy II svn repository, then a copy of https://ptolemy.berkeley.edu/accessors is checked out in If you check out the ptII svn repository via anonymous, read-only access, then the copy of https://ptolemy.berkeley.edu/accessors will be checked out anonymously. If you check out the ptII svn repository via read/write access, then the copy of the https://ptolemy.berkeley.edu/accessors will be checked out read-write if you have read/write access to that repo. The copy of https://ptolemy.berkeley.edu/accessors is checked out when So, to get started, either run: cd $PTII ./configure $PTII/bin/vergil org/terraswarm/accessor/demo/StockTick/StockTick.xml This will create or update bash-3.2$ ls $PTII/org/terraswarm/accessor/accessors/web Accessor_1.dtd cameras jsdoc svn.html README.md copyright.txt localization updateIndex audio demo net utilities basicFunctions.js devices obsolete vendors basicFunctionsPtDoc.xml doc renderHTML.xsl wiki browserHost.js image robotics browserHostPtDoc.xml index.html services build.xml index.json signals bash-3.2$ index.json file and there are subdirectories.
When to create an AccessorAccessors are to encapsulate non-trivial bits of functionality that are suitable for reuse. The same holds for modules, but a module may be host-dependent, whereas an accessor may not. For simple functionality (such as a ramp) just use JavaScript. Note that the Cape Code Host can have both JSAccessor actors (which are wrappers for accessors) and JavaScript actors, which can be used for simple functionality. Overview of AccessorsIf you are not yet familiar with Accessors, see Getting Started. In particular, see Publications And Presentations The quotes below are from the web page:
The good news is that:
The less good news is that modules names in the Java-based Cape Code Host and the Node-based Node Host differ:
Another minor issue is that Java does not support
The Big Picture
Files to ModifyWe are adding files that define the Node.js module implementation for web-socket-client and web-socket-server.
Future work:
File ModificationsMake the following edits to
Make the following edits to
FIXME: we need to deal with the EventEmitter Adding the ws moduleWe will be using the ws Node.js interface, so we need to add update var WebSocket = require('ws'); While in the npm install ws This should create We will check the modules in to the svn repo after everything is working. supportedReceiveTypes()In the Cape Code Host, the webSocketClient.js file has this method: /////////////////////////////////////////////////////////////////////////////// //// supportedReceiveTypes /** Return an array of the types supported by the current host for * receiveType arguments. */ exports.supportedReceiveTypes = function () { return WebSocketHelper.supportedReceiveTypes(); }; The above Cape Code Host code will not work for us because it uses the Nashorn Java var bash-3.2$ bash-3.2$ cat Test.java import ptolemy.actor.lib.jjs.modules.webSocket.WebSocketHelper; import java.util.Arrays; public class Test { public static void main (String args []) { System.out.println(Arrays.toString(WebSocketHelper.supportedReceiveTypes())); } } bash-3.2$ javac -classpath $PTII:. Test.java bash-3.2$ $PTII/bin/ptinvoke Test [application/json, text/plain, JPG, jpg, bmp, BMP, gif, GIF, WBMP, png, PNG, jpeg, wbmp, JPEG] bash-3.2$ Thus, method in the Node Host file /////////////////////////////////////////////////////////////////////////////// //// supportedReceiveTypes /** Return an array of the types supported by the current host for * receiveType arguments. */ exports.supportedReceiveTypes = function () { // These values are based on what Cape Code returns in // ptolemy.actor.lib.jjs.modules.webSocket.WebSocketHelper.supportedReceiveTypes(). // Not sure about the validity of 'JPG' and subsequent args. return ['application/json', 'text/plain', 'JPG', 'jpg', 'bmp', 'BMP', 'gif', 'GIF', 'WBMP', 'png', 'PNG', 'jpeg', 'wbmp', 'JPEG']; }; supportedSendTypes()If we update /////////////////////////////////////////////////////////////////////////////// //// supportedSendTypes /** Return an array of the types supported by the current host for * sendType arguments. */ exports.supportedSendTypes = function () { // These values are based on what Cape Code returns in // ptolemy.actor.lib.jjs.modules.webSocket.WebSocketHelper.supportedSendTypes(). // FIXME: Not sure about the validity of 'JPG' and subsequent args. return ['application/json', 'text/plain', 'JPG', 'jpg', 'bmp', 'BMP', 'gif', 'GIF', 'WBMP', 'png', 'PNG', 'jpeg', 'wbmp', 'JPEG']; }; util.inherits()It is good to sanity check our work by parsing the file. Jumping past the Client constructor, there is a line that needs to be commented out so that the Node Host can parse this file. The Cape Code Host includes the following line after the Client constructor: util.inherits(exports.Client, EventEmitter); However, because we have no util module in the Node Host, when we invoke bash-3.2$ node webSocketClient/webSocketClient.js /Users/cxh/ptII/org/terraswarm/accessor/accessors/web/{Main.HowToCreateANodeModule$HOSTS_NODE}/webSocketClient/webSocketClient.js:175 util.inherits(exports.Client, EventEmitter); ^ ReferenceError: util is not defined at Object.<anonymous> (/Users/cxh/ptII/org/terraswarm/accessor/accessors/web/{Main.HowToCreateANodeModule$HOSTS_NODE}/webSocketClient/webSocketClient.js:175:1) at Module._compile (module.js:413:34) at Object.Module._extensions..js (module.js:422:10) at Module.load (module.js:357:32) at Function.Module._load (module.js:314:12) at Function.Module.runMain (module.js:447:10) at startup (node.js:141:18) at node.js:933:3 bash-3.2$ FIXME: Not sure what to do here. Do we define a Solution var util = require('util'); In the short term, we comment it out // FIXME: Not sure what to do with util.inherits. // See {Main.HowToCreateANodeModule$ACCESSORS_HOME}/wiki/Main/HowToCreateANodeAccessor#utilInherits //util.inherits(exports.Client, EventEmitter); Does it parse?As a quick test, we run node on the module file to see if it parses: bash-3.2$ pwd /Users/cxh/ptII/org/terraswarm/accessor/accessors/web/{Main.HowToCreateANodeModule$HOSTS_NODE} bash-3.2$ node node_modules/webSocketClient/webSocketClient.js bash-3.2$ The above was successful, there were no parse errors. Composite Accessor testNext up, we create a composite Accessor that contains a WebSocketClient and try to run it.
Success! We can instantiate a WebSocket accessor using our module. However, it does not actually do anything. Client constructor.Next up in the Cape Code implementation is the declaration of a Client constructor: /////////////////////////////////////////////////////////////////////////////// //// Client /** Construct an instance of a socket client that can send or receive messages * to a server at the specified host and port. * The returned object subclasses EventEmitter. * You can register handlers for events 'open', 'message', 'close', or 'error'. * The event 'open' will be emitted when the socket has been successfully opened. * The event 'message' will be emitted with the body of the message as an * argument when an incoming message arrives on the socket. * You can invoke the send() function to send data to the server. * * The type of data sent and received can be specified with the 'sendType' * and 'receiveType' options. * In principle, any MIME type can be specified, but the host may support only * a subset of MIME types. The client and the server have to agree on the type, * or the data will not get through correctly. * * The default type for both sending and receiving * is 'application/json'. The types supported by this implementation * include at least: * * __application/json__: The this.send() function uses JSON.stringify() and sends the * result with a UTF-8 encoding. An incoming byte stream will be parsed as JSON, * and if the parsing fails, will be provided as a string interpretation of the byte * stream. * * __text/\*__: Any text type is sent as a string encoded in UTF-8. * * __image/x__: Where __x__ is one of __json__, __png__, __gif__, * and more (FIXME: which, exactly?). * In this case, the data passed to this.send() is assumed to be an image, as encoded * on the host, and the image will be encoded as a byte stream in the specified * format before sending. A received byte stream will be decoded as an image, * if possible. FIXME: What happens if decoding fails? * * The event 'close' will be emitted when the socket is closed, and 'error' if an * an error occurs (with an error message as an argument). * For example, * <pre> * var WebSocket = require('webSocketClient'); * var client = new WebSocket.Client({'host': 'localhost', 'port': 8080}); * client.send({'foo': 'bar'}); * client.on('message', function(message) { * console.log('Received from web socket: ' + message); * }); * client.open(); * </pre> * * The above code may send a message even before the socket is opened. This module * implementation will queue that message to be sent later when the socket is opened. * * The options argument is a JSON object that can contain the following properties: * * host: The IP address or host name for the host. Defaults to 'localhost'. * * port: The port on which the host is listening. Defaults to 80. * * receiveType: The MIME type for incoming messages, which defaults to 'application/json'. * * sendType: The MIME type for outgoing messages, which defaults to 'application/json'. * * connectTimeout: The time to wait before giving up on a connection, in milliseconds * (defaults to 1000). * * numberOfRetries: The number of times to retry connecting. Defaults to 10. * * timeBetweenRetries: The time between retries, in milliseconds. Defaults to 500. * * discardMessagesBeforeOpen: If true, discard messages before the socket is open. Defaults to false. * * throttleFactor: The number milliseconds to stall for each item that is queued waiting to be sent. Defaults to 0. * * @param options The options. */ exports.Client = function (options) { options = options || {}; this.port = options.port || 80; this.host = options.host || 'localhost'; this.sslTls = options.sslTls || false; this.receiveType = options.receiveType || 'application/json'; this.sendType = options.sendType || 'application/json'; this.connectTimeout = options.connectTimeout || 1000; this.numberOfRetries = options.numberOfRetries || 10; this.timeBetweenRetries = options.timeBetweenRetries || 500; this.trustAll = options.trustAll || false; this.trustedCACertPath = options.trustedCACertPath || ''; this.discardMessagesBeforeOpen = options.discardMessagesBeforeOpen || false; this.throttleFactor = options.throttleFactor || 0; this.helper = WebSocketHelper.createClientSocket( this, this.host, this.sslTls, this.port, this.receiveType, this.sendType, this.connectTimeout, this.numberOfRetries, this.timeBetweenRetries, this.trustAll, this.trustedCACertPath, this.discardMessagesBeforeOpen, this.throttleFactor); }; The above needs to be updated to be a call to the appropriate See the ws WebSocket documentation for how to create a WebSocket, which is the closest to the Cape Code WebSocketHelper createClientSocket().
See Also
Create a copy of an accessorOne way to learn about accessors is to copy something that works and use it. In this example, we will create a copy of the StockTick accessor
Import the New Accessor
Use the New AccessorNote that the name has changed, but the functionality is the same as StockTick accessor. You could
Change the Functionality.
ModulesSome Accessors require JavaScript modules, which, in Ptolemy, are found in var discovery = require('discovery'); If you did the above steps and created var discovery = require('discovery'); with var discovery = require('myDiscovery'); and then create a copy of cd $PTII/ptolemy/actor/lib/jjs/modules mkdir myDiscovery cp discovery/discovery.js myDiscovery/myDiscovery.js cp discovery/package.json myDiscovery/package.json discovery to myDiscovery .
|