Code Search for Developers
 
 
  

McFlashConnect.as from Make Controller at Krugle


Show McFlashConnect.as syntax highlighted

/*********************************************************************************
 Copyright 2006 MakingThings

 Licensed under the Apache License, 
 Version 2.0 (the "License"); you may not use this file except in compliance 
 with the License. You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0 

 Unless required by applicable law or agreed to in writing, software distributed
 under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 CONDITIONS OF ANY KIND, either express or implied. See the License for
 the specific language governing permissions and limitations under the License.
 
*********************************************************************************/

import com.makingthings.makecontroller.OscMessage;
import com.makingthings.makecontroller.AddressHandler;
import com.makingthings.makecontroller.Board;
import mx.utils.Delegate;

/** 
	<p>Mchelper (Make Controller Helper) is a separate application that must be run simultaneously with your Flash application.
	Because Flash can't connect to external devices by itself, Mchelper is a necessary intermediate step - 
	it can connect to the Make Controller over the network and via USB, and then it formats communication with the board
	into XML that can be fed in and out of Flash.  <b>McFlashConnect</b> makes a connection to Mchelper from your Flash movie.</p>
	
	<p>The general idea is that you:
		<ol>
			<li>Connect to mchelper.</li>
			<li>Receive info about which boards are connected.</li>
			<li>Send and/or receive messages to any of those boards.</li>
			<li>Receive info when mchelper has disconnected.</li>
		</ol>
	</p>
	
	<p>McFlashConnect emits a few events of interest - check these out at in the in the 
	<a href="McEvent.html">McEvent</a> section.</p>
	
	<p>The easiest way to get started is to check out a few of the example files included in the MakingThings download, and start
	modifying things to fit your project.</p>
*/

class com.makingthings.makecontroller.McFlashConnect 
{
	/** The IP address of the machine running Mchelper. 
		<p>Since this usually on the same machine you're running your Flash app on, 
		it's usually best to set this to <b>localhost</b></p> */
	public var mchelperAddress;
	/** The port that Mchelper is listening for connections from McFlashConnect on. Default is <b>11000</b>.*/
	public var mchelperPort;
	private var defaultBoard:Board;
	private var registeredAddresses:Array; // list of registered addresses and functions to call if a message with that address shows up
	private var connectedBoards:Array;
	private var mySocket:XMLSocket;
	public var connected:Boolean; 
	
  /**
		Create a new instance of McFlashConnect.
		*/
  function McFlashConnect( )
  {
		connected = false;
		mchelperAddress = "localhost";
		mchelperPort = 11000;
		registeredAddresses = new Array( );
		connectedBoards = new Array( );
  }
	
	/**
	Register a handler for messages with a given OSC address.
	
	<p>You can create a function of your own that will get called whenever an incoming OSC message matches
	a particular address.  This saves you from checking the address of all incoming OSC messages. </p>
	
	<p>Your callback should be in the form:
	<pre> myCallback( msg:OscMessage ); </pre></p>
	
	<h3>Example</h3>
	<pre>
	mcflash.addAddressListener( "/analogin/7/value", onTrimpot );
	
	// this will get called whenever a message from "/analogin/7/value" is received.
	function onTrimpot( msg:OscMessage ) 
	{
		trace( "new OSC message: " + msg.toString() );
	}
	</pre>
	
	@param addr The OSC address to match.
	@param callback The function that you want to be called back on.
	*/
	public function addAddressListener( addr:String, callback:Function )
	{
		var handler = new AddressHandler( addr, callback );
		registeredAddresses.push( handler );
	}
	
	/**
	Set the board to send messages to when the send( ) method is used.  If no default board
	is set, you need to specify which board to send to using sendToBoard( ). It can be 
	convenient to set this when a new board arrives.
	
	<h3>Example</h3>
	<pre>
	function onBoardArrived( event:McEvent )
	{
		var newBoard:Board == event.data;
		mcFlash.setDefaultBoard( newBoard );
	}
	</pre>
	
	@param board A <b>Board</b> object indicating which board to send to.
	*/
	public function setDefaultBoard( board:Board ) : Void
	{
		defaultBoard = board;
	}
	
	/**
	From the list of connected boards, find a board with a given IP address.
	
	<h3>Example</h3>
	<pre>
	// find a board with the IP address 192.168.0.200
	var myBoard:Board = getBoardByIPAddress( "192.168.0.200" );
	</pre>
	
	@param address A string specifying the IP address of the board to find.
	@return The <b>Board</b> object with the given IP address, or null if it wasn't found.
	*/
	public function getBoardByIPAddress( address:String ) : Board
	{
		var boardCount:Number = connectedBoards.length;
		for( var i = 0; i < boardCount; i++ )
		{
			if( connectedBoards[ i ].location == address )
				return connectedBoards[ i ];
		}
		return null;
	}
	
	/**
	From the list of connected boards, find a board at a given USB location.  On Windows,
	this will be a COM port, otherwise it will be the device location.
	
	<h3>Example</h3>
	<pre>
	// find a board with the USB location COM9
	var myBoard:Board = getBoardByUsbLocation( "COM9" );
	</pre>
	
	@param location A string specifying the USB location of the board to find.
	@return The <b>Board</b> object with the given USB location, or null if it wasn't found.
	*/
	public function getBoardByUsbLocation( location:String ) : Board
	{
		var boardCount:Number = connectedBoards.length;
		for( var i = 0; i < boardCount; i++ )
		{
			if( connectedBoards[ i ].location == location )
				return connectedBoards[ i ];
		}
		return null;
	}
	
	/**
	From the list of connected boards, find all boards with a given name.  Because many
	boards can potentially have the same name, this will return an Array of <b>Board</b> objects
	with the given name.
	
	<h3>Example</h3>
	<pre>
	// find all boards named "Make Controller Kit"
	var myBoards:Array = getBoardsByName( "Make Controller Kit" );
	</pre>
	
	@param name A string specifying the name of the board to find.
	@return An Array with all the boards with the given name.  If no boards were found, this Array will be empty.
	*/
	public function getBoardsByName( name:String ) : Array
	{
		var boardCount:Number = connectedBoards.length;
		var boardArray:Array= [];
		for( var i = 0; i < boardCount; i++ )
		{
			if( connectedBoards[ i ].name == name )
				boardArray.push( connectedBoards[ i ] );
		}
		return boardArray;
	}
	
	/**
	From the list of connected boards, find all boards with a given serial number.  Because many
	boards can potentially have the same serial number, this will return an Array of <b>Board</b> objects
	with the given number.
	
	<h3>Example</h3>
	<pre>
	// find all boards with the serial number 18934
	var myBoards:Array = getBoardsBySerialNumber( 18934 );
	</pre>
	
	@param serialnum A Number specifying the serial number of the board to find.
	@return An Array with all the boards with the given serial number.  If no boards were found, this Array will be empty.
	*/
	public function getBoardsBySerialNumber( serialnum:Number ) : Array
	{
		var boardCount:Number = connectedBoards.length;
		var boardArray:Array= [];
		for( var i = 0; i < boardCount; i++ )
		{
			if( connectedBoards[ i ].serialNumber == serialnum )
				boardArray.push( connectedBoards[ i ] );
		}
		return boardArray;
	}
	
	
	// ************************************************************************************************************************************
	// OSC stuff
	// ************************************************************************************************************************************
	
	// ** parse messages from an XML-encoded OSC packet
	private function parseMessages( node:XMLNode ) : Void //this is called when we get OSC packets back from FLOSC.
	{	
		var time:Number = node.attributes.TIME;
		var addr:String = node.attributes.ADDRESS;
		var message:XMLNode = node.firstChild;
		while( message != null )
		{
			if (message.nodeName == "MESSAGE")
			{
				var msgName:String = message.attributes.NAME;		
				var oscData:Array = [];
				for (var child:XMLNode = message.firstChild; child != null; child=child.nextSibling)
				{
					if (child.nodeName == "ARGUMENT")
					{
						var type:String = child.attributes.TYPE;
						//float
						if (type=="f") {
							oscData.push(parseFloat(child.attributes.VALUE));
						} else 
						// int
						if (type=="i") {
							oscData.push(parseInt(child.attributes.VALUE));
						} else 
						//string
						if (type=="s") {
							oscData.push(child.attributes.VALUE);
						}	
						//trace( "Address: " + node.attributes.NAME + ", Arg: " + child.attributes.VALUE );
					}
				}
				var msg:OscMessage = new OscMessage( msgName, oscData );
				msg.from = addr;
				msg.time = time;
				//trace( "name: " + msg.address + ", arg: " + msg.args[0] + ", from: " + msg.fromIpAddress );
				doCallback( msg );
				message = message.nextSibling; // move to the next MESSAGE node, if there is one
			}
		}
	}
	
	private function doCallback( msg:OscMessage ) : Void
	{
		var knownAddresses:Number = registeredAddresses.length;
		var calledBack:Boolean = false;
		for( var i = 0; i < knownAddresses; i++ )
		{
			if( registeredAddresses[i].address == msg.address )
			{
				registeredAddresses[i].callback( msg );
				calledBack = true;
			}
		}
		if( !calledBack )
			onMessageIn( msg );
	}
	
	/**
	Get called back on this function with any incoming OSC messages.
	
	<p>Whenever a new OSC message arrives, this function will be called, and you can process the message
	however you like.</p>
	<p>Most often, you'll want to test the address of the incoming message to see if it's something you're interested in.
	So if you want to listen for the trimpot (analogin 7), for exmaple, you might implement it like...</p>
	
	<h3>Example</h3>
	<pre> 	
	mcflash.onMessageIn = function( msg:OscMessage )
	{
		if( msg.address == "/analogin/7/value" )
			var trimpot = msg.args[0]; // the value is in the first argument of the message
	}
	</pre>
	<p>Now the <b>trimpot</b> variable holds the value of the trimpot and you can do whatever you like with it. </p>
	<p>See the <a href="OscMessage.html">OscMessage</a> class for more details.</p>
 	*/
	public function onMessageIn( msg:OscMessage )
	{
	}
	
	/**
	Get notified when a new board is connected.
	
	<p>When a new board is connected, you may want to set it as the default board so that you don't have to store
	a reference to it yourself.  You might want to check its <b>type</b> first.</p>
	
	<h3>Example</h3>
	<pre> 	
	mcflash.onBoardArrived = function( board:Board )
	{
		if( board.type == "USB" )
			mcflash.setDefaultBoard( board );
	}
	</pre>
	
	<p>Or you might want to make sure it has a certain address...</p>
	
	<pre> 	
	mcflash.onBoardArrived = function( board:Board )
	{
		if( board.location == "192.168.0.121" )
			mcflash.setDefaultBoard( board );
	}
	</pre>
	*/
	public function onBoardArrived( board:Board )
	{
		//trace( "New board connected: " + board.location );
	}
	
	/** 
	Get notified when a board is disconnected.
	
	<h3>Example</h3>
	<pre> 	
	mcflash.onBoardRemoved = function( board:Board )
	{
		trace( "Board disconnected at " + board.location );
	}
	</pre>
	*/
	public function onBoardRemoved( board:Board )
	{
		//trace( "Board removed: " + board.location );
	}
	
	/** 
	Get notified when McFlashConnect could not connect to mchelper.
	
	<h3>Example</h3>
	<pre> 	
	mcflash.onConnectError = function( )
	{
		trace( "Failed to connect to mchelper." );
	}
	</pre>
	*/
	public function onConnectError( )
	{
		
	}
	
	/** 
	Get notified when McFlashConnect has connected successfully to mchelper.
	
	<h3>Example</h3>
	<pre> 	
	mcflash.onConnect = function( )
	{
		trace( "Successfully connected to mchelper." );
	}
	</pre>
	*/
	public function onConnect( )
	{
		
	}
	
	/**
	Connect McFlashConnect up to Mchelper.
	This will connect using the current values of <b>mchelperAddress</b> and <b>mchelperPort</b>.
	
	<h3>Example</h3>
	<pre> 
	mcflash.connect( );
	</pre>
	*/
	public function connect( )
	{
		mySocket = new XMLSocket();
		mySocket.onConnect = Delegate.create( this, handleConnect );
		//mySocket.onClose = handleClose;
		mySocket.onXML = Delegate.create( this, handleIncoming );
	
		if (!mySocket.connect(mchelperAddress, mchelperPort))
			onConnectError( );
	}
	
	/**
	Disconnect from mchelper.
	
	<p>This closes the XML connection to mchelper.  This does not need to be called explicitly before closing your movie.</p>
	
	<h3>Example</h3>
	<pre> 
	mcflash.disconnect( );
	</pre>
	*/
	public function disconnect( ) 
	{
		if( connected )
		{
			mySocket.close();
			connected = false;
		}
		else
			return;
	}
	
	// *** event handler for incoming XML-encoded OSC packets
  private function handleIncoming (xmlIn)
	{
		// parse out the packet information
		var xmlDoc:XML = new XML( );
		xmlDoc.ignoreWhite = true;
		xmlDoc.parseXML( xmlIn );
		var n:XMLNode = xmlDoc.firstChild;
		//trace( xmlDoc );
		if( n != null )
		{
			if( n.nodeName == "OSCPACKET" )
				parseMessages( n );
			else if( n.nodeName == "BOARD_ARRIVAL" || n.nodeName == "BOARD_REMOVAL" )
				updateBoardList( n );
			else if( n.nodeName == "BOARD_INFO" )
				updateBoardInfo( n );
		}
	}
	
	private function updateBoardList( xml:XMLNode ):Void
	{
		var boardMessage:XMLNode = xml.firstChild;
		if( xml.nodeName == "BOARD_ARRIVAL" )
		{
			while( boardMessage != null )
			{
				var newBoard:Board = new Board( boardMessage.attributes.TYPE, boardMessage.attributes.LOCATION );
				if( boardMessage.attributes.NAME )
					newBoard.name = boardMessage.attributes.NAME;
				if( boardMessage.attributes.SERIALNUMBER )
					newBoard.serialnumber = int( boardMessage.attributes.SERIALNUMBER );
				connectedBoards.push( newBoard );
				onBoardArrived( newBoard );
				boardMessage = boardMessage.nextSibling;
			}
		}
		else if( xml.nodeName == "BOARD_REMOVAL" )
		{
			while( boardMessage != null )
			{
				for( var i = 0; i < connectedBoards.length; i++ )
				{
					if( connectedBoards[i].location == boardMessage.attributes.LOCATION )
					{
						var newBoard:Board = connectedBoards[i];
						connectedBoards.splice( i, 1 );
						onBoardRemoved( newBoard );
					}
				}
				boardMessage = boardMessage.nextSibling;
			}
		}
	}
	
	private function updateBoardInfo( xml:XMLNode ) : Void
	{
		var boardMessage:XMLNode = xml.firstChild;
		while( boardMessage != null )
		{
			if( boardMessage.nodeName == "BOARD" )
			{
				for( var i = 0; i < connectedBoards.length; i++ )
				{
					var board:Board = connectedBoards[i];
					if( board.location == boardMessage.attributes.LOCATION )
					{
						board.name = boardMessage.attributes.NAME;
						board.serialnumber = int( boardMessage.attributes.SERIALNUMBER );
					}
				}
			}
			boardMessage = boardMessage.nextSibling;
		}
	}
	
	// *** event handler to respond to successful connection attempt
	private function handleConnect (succeeded)
	{
		if( succeeded == true )
		{
			this.connected = true;
			onConnect( );
		}
		else
			onConnectError( );
	}
	
	/**
	* Send a message to the board.
	<p>This will send a message to the current <b>defaultBoard</b>, as set by setDefaultBoard( ).
	If you need to specify the board to send to, use sendToBoard( ).</p>
	
	<h3>Example</h3>
	<pre> 
	// Specify the OSC address and argument to send - turn on LED 1
	mcflash.send( "/appled/1/state", [1] );
	
	// Or, to read the state of the LED...
	mcflash.send( "/appled/1/state", [] );
	</pre>
	
	@param address The OSC address to send to, as type \b String.
	@param arg The value to be sent.  Send an empty Array if there are no arguments.
	@see sendToBoard( )
	*/
	public function send( address:String, args:Array )
	{
		sendToBoard( address, args, defaultBoard );
	}
	
	/**
	* Send a message to a given board. 
	
	<h3>Example</h3>
	<pre> 
	// Specify the OSC address and argument to send - turn on LED 1
	var myBoard:Board = mcflash.getBoardByIPAddress( "192.168.0.200" );
	mcflash.sendToBoard( "/appled/1/state", [1], myBoard );
	</pre>
	
	@param address The OSC address of your message.
	@param args The value(s) to be sent.
	@param board The board to send to.
	*/
	public function sendToBoard( address:String, args:Array, board:Board )
	{
		if( board == null )
			return;
		
		var xmlOut:XML = new XML();
		var packetOut = createPacketOut( xmlOut, 0, board.location );
		var xmlMessage = createMessage( xmlOut, packetOut, address );
		parseArguments( xmlOut, xmlMessage, args );
		
		xmlOut.appendChild(packetOut);
	
		if( mySocket && this.connected )
			mySocket.send(xmlOut);
	}
	
	// used internally to prep an XML object to be sent out.
	private function createPacketOut( xmlOut:XML, time:Number, destination:String ):XMLNode
	{
		var packetOut = xmlOut.createElement("OSCPACKET");
		packetOut.attributes.TIME = 0;
		packetOut.attributes.PORT = 0;
		packetOut.attributes.ADDRESS = destination;
		
		return packetOut;
	}
	
	// used internally to create a message element within the xmlOut object
	private function createMessage( xmlOut:XML, packetOut:XMLNode, address:String ):XMLNode
	{
		var xmlMessage = xmlOut.createElement("MESSAGE");
		xmlMessage.attributes.NAME = address;
		packetOut.appendChild(xmlMessage);
		return xmlMessage;
	}
	
	// used internally to determine the type of an argument, and append it to its corresponding message in the outgoing XML object.
	private function parseArguments( xmlOut:XML, xmlMessage:XMLNode, args:Array )
	{
		var argument:XMLNode;
		var argsLength:Number = args.length;
		// NOTE : the server expects all strings to be encoded with the escape function.
		for( var i = 0; i < argsLength; i++ )
		{
			argument = xmlOut.createElement("ARGUMENT");
			var argInt = parseInt(args[i]);
			if(isNaN(argInt))
			{
				argument.attributes.TYPE = "s";
				argument.attributes.VALUE = escape(args[i]);
			} 
			else
			{
				var argString:String = args[i].toString();
				var stringLength:Number = argString.length;
				var float:Boolean = false;
				for( var j = 0; j < stringLength; j++ )
				{
					if( argString.charAt( j ) == "." )
					{
						argument.attributes.TYPE="f";
						argument.attributes.VALUE=parseFloat(argString);
						float = true;
						break;
					}
				}
				if( !float )
				{
					argument.attributes.TYPE="i";
					argument.attributes.VALUE=parseInt(args[i]);
				}
			}
			xmlMessage.appendChild(argument);
		}
	}
	
	/**
	* Send an <b>OscMessage</b> to a board. 
	<p>This will send a message, of type <b>OscMessage</b>, to the current <b>defaultBoard</b>.
	If you need to specify another board to send to, use sendMessageToBoard( ). </p>
	
	<h3>Example</h3>
	<pre> 
	// create an OscMessage
	var turnOnLed:OscMessage = new OscMessage( "/appled/0/state", [1] );
	// now send it
	mcflash.sendMessage( turnOnLed );
	</pre>
	
	@param oscM The message, of type <b>OscMessage</b>, to be sent
	@see sendMessageToBoard( )
	*/
	public function sendMessage( oscM:OscMessage )
	{
		sendMessageToBoard( oscM, defaultBoard );
	}
	
	/**
	* Send an OscMessage to a given board. 
	
	<h3>Example</h3>
	<pre> 
	// create an OscMessage
	var turnOffLed:OscMessage = new OscMessage( "/appled/0/state", [0] );
	// now send it to a specified address
	var myBoard:Board = mcflash.getBoardByIPAddress( "192.168.0.200" );
	mcflash.sendMessageToAddress( turnOffLed, myBoard );
	</pre>
	
	@param oscM The message, of type OscMessage, to be sent
	@param board The board to send to.
	*/
	public function sendMessageToBoard( oscM:OscMessage, board:Board )
	{
		if( board == null )
			return;	

		var xmlOut:XML = new XML();
		var packetOut = createPacketOut( xmlOut, 0, board.location );
		var xmlMessage = createMessage( xmlOut, packetOut, oscM.address );
		parseArguments( xmlOut, xmlMessage, oscM.args );

		xmlOut.appendChild(packetOut);
	
		if( mySocket && this.connected )
			mySocket.send(xmlOut);
	}
	
	/**
	Send an OscBundle to the board.
	<p>This will send a bundle to the current <b>defaultBoard</b>.
	If you need to specify another board to send to, use sendBundleToBoard( ). </p>
	
	<p>It's a good idea to send bundles when possible, in order to reduce the traffic between the board and Flash.
	If you need to specify the address and port for each message, use sendBundleToAddress( ).</p>
	
	<h3>Example</h3>
	<pre>
	// create an Array (this Array will be our OscBundle)
	// and stuff our messages into it
	var myOscBundle:Array = new Array( );
	
	// create a couple of OscMessages
	myOscBundle.push( new OscMessage( "/appled/0/state", [0] ) ); // turn off LED 0
	myOscBundle.push( new OscMessage( "/analogin/0/value", [] ) ); // ask for analogin 0

	// now send it
	mcflash.sendBundle( myOscBundle );
	</pre>
	
	@param oscB The OscBundle to be sent
	@see sendBundleToBoard( ) 
	*/
	public function sendBundle( oscB:Array )
	{
		sendBundleToBoard( oscB, defaultBoard );
	}
	
	/**
	Send an OscBundle to a given board. 
	
	<h3>Example</h3>
	<pre> 
	// create an Array (this Array will be our OscBundle)
	// and stuff our messages into it
	var myOscBundle:Array = new Array( );
	
	// create a couple of OscMessages
	myOscBundle.push( new OscMessage( "/appled/0/state", [0] ) ); // turn off LED 0
	myOscBundle.push( new OscMessage( "/analogin/0/value", [] ) ); // ask for analogin 0
	
	// now send it to a specified board
	var myBoard:Board = mcflash.getBoardByIPAddress( "192.168.0.200" );
	mcflash.sendBundleToAddress( myOscBundle, myBoard );
	</pre>
	
	@param oscB The OscBundle to be sent.
	@param board The board to send to.
	*/
	public function sendBundleToBoard( oscB:Array, board:Board )
	{
		if( board == null )
			return;
				
		var xmlOut:XML = new XML();
		var packetOut = createPacketOut( xmlOut, 0, board.location );
		for( var i = 0; i < oscB.length; i++ )
		{
			if( oscB[i] instanceof OscMessage == false )
			{
				trace( "Error - Item #" + i + " in the OscBundle was not an OscMessage...the entire bundle was not sent." );
				return;
			}

			var oscM:OscMessage = oscB[i];
			//trace( "Message " + i + ", address " + oscM.address + ", arg: " + oscM.args[0] );
			var xmlMessage = createMessage( xmlOut, packetOut, oscM.address );
			parseArguments( xmlOut, xmlMessage, oscM.args );
		}

		xmlOut.appendChild(packetOut);
	
		if( mySocket && this.connected )
			mySocket.send(xmlOut);
	}
}








See more files for this project here

Make Controller

The Make Controller is an open microcontroller platform for Makers of all kinds, by MakingThings. This project maintains the official firmware for the board and source for the software tools used with the board.

Project homepage: http://sourceforge.net/projects/makingthings
Programming language(s): C,C#,C++
License: other

  AddressHandler.as
  Board.as
  McFlashConnect.as
  OscMessage.as
  index.as