<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>entremidietdeux.com</title>
	<atom:link href="http://www.entremidietdeux.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.entremidietdeux.com</link>
	<description>Actionscript 3, Flash, Flex &#38; more.</description>
	<lastBuildDate>Fri, 13 Jan 2012 17:59:31 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.4</generator>
		<item>
		<title></title>
		<link>http://www.entremidietdeux.com/126/</link>
		<comments>http://www.entremidietdeux.com/126/#comments</comments>
		<pubDate>Fri, 13 Jan 2012 17:59:31 +0000</pubDate>
		<dc:creator>fh</dc:creator>
				<category><![CDATA[pub-copinage]]></category>

		<guid isPermaLink="false">http://www.entremidietdeux.com/?p=126</guid>
		<description><![CDATA[]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.entremidietdeux.com/wp-content/uploads/2012/01/13192598.jpg"><img src="http://www.entremidietdeux.com/wp-content/uploads/2012/01/13192598.jpg" alt="" title="13192598" width="400" height="400" class="alignnone size-full wp-image-127" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.entremidietdeux.com/126/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Découverte du framework Robotlegs [5/5]</title>
		<link>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-55/</link>
		<comments>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-55/#comments</comments>
		<pubDate>Fri, 12 Nov 2010 17:59:04 +0000</pubDate>
		<dc:creator>fh</dc:creator>
				<category><![CDATA[Actionscript 3]]></category>
		<category><![CDATA[Flash Platform]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[actionscript]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[robotlegs]]></category>

		<guid isPermaLink="false">http://www.entremidietdeux.com/?p=111</guid>
		<description><![CDATA[La couche Controller Les Events Ici nous avons seulement un unique Event pour ce projet. C’est assez rare, le projet est vraiment petit. [as3] package controller.events { import model.vo.Entry; import flash.events.Event; public class DiaporamaEvent extends Event { // événements concernant &#8230; <a href="http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-55/">Continuer la lecture <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<h3>La couche Controller</h3>
<h4>Les Events</h4>
<p><img src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/rl-flag-sm.png" alt="" title="rl-flag-sm" width="69" height="90" class="alignright size-full wp-image-82" /><br />
Ici nous avons seulement un unique Event pour ce projet. C’est assez rare, le projet est vraiment petit. </p>
<p>[as3]<br />
package controller.events<br />
{<br />
	import model.vo.Entry;</p>
<p>	import flash.events.Event;</p>
<p>	public class DiaporamaEvent extends Event<br />
	{<br />
		// événements concernant l&#8217;application<br />
		public static const STARTUP : String = &#8216;startup&#8217;;<br />
		public static const CONFIG_COMPLETE : String = &#8216;configComplete&#8217;;<br />
		public static const NEXT : String = &#8216;next&#8217;;<br />
		public static const PREVIOUS : String = &#8216;previous&#8217;;</p>
<p>		// événements concernant la View<br />
		public static const REFRESH : String = &#8216;refresh&#8217;;<br />
		public static const LOAD_COMPLETE : String = &#8216;loadComplete&#8217;;		</p>
<p>		public var entry : Entry;</p>
<p>		public function DiaporamaEvent( type : String,<br />
					    bubbles : Boolean = false,<br />
					    cancelable : Boolean = false,<br />
					    entry : Entry = null )<br />
		{<br />
			super(type, bubbles, cancelable);</p>
<p>			this.entry = entry;<br />
		} </p>
<p>		public override function clone():Event<br />
		{<br />
			return new DiaporamaEvent( type, bubbles, cancelable, entry );<br />
		} </p>
<p>		public override function toString():String<br />
		{<br />
			return formatToString(&laquo;&nbsp;DiaporamaEvent&nbsp;&raquo;, &laquo;&nbsp;type&nbsp;&raquo;, &laquo;&nbsp;bubbles&nbsp;&raquo;, &laquo;&nbsp;cancelable&nbsp;&raquo;, &laquo;&nbsp;eventPhase&nbsp;&raquo;);<br />
		}</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<p>On distingue 2 catégories d’Event.TYPE dans cette classe. On a vu plus haut que REFRESH ne concerne qu’un Mediator, c’est la même chose pour LOAD_COMPLETE. J’ai donc bien distingué ces deux catégories dans mes commentaires.<span id="more-111"></span></p>
<p>En effet, si tous les Event donnant lieu à une Command sont listés dans mon Context, les handlers présents dans les Mediators sont moins facilement repérables. Cela m’aidera à me relire que de le préciser dans ma classe <strong>DiaporamaEvent</strong>.</p>
<h4>Les Commands</h4>
<p>Les Commands contiennent la logique de l’application, ici nos seules fonctionnalités sont les clics sur <em>suivant</em> et <em>précédent</em>. On retrouve donc ces 2 Commands, prenons-en une sur les 2, elles sont très semblables : </p>
<p>[as3]<br />
package controller.commands<br />
{<br />
	import model.DiaporamaData;</p>
<p>	import org.robotlegs.mvcs.Command;</p>
<p>	public class NextCommand extends Command<br />
	{<br />
		[Inject]<br />
		public var diaporamaData : DiaporamaData;</p>
<p>		override public function execute() : void<br />
		{<br />
			diaporamaData.next();<br />
		}</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<p>Lorsque l’événement <strong>DiaporamaEvent.NEXT</strong> survient, la Command est éxecutée, le Model est injecté, et on agit sur le Model par un appel de méthode publique. Voilà qui colle plutôt bien à la définition du Controller qu’on a donnée plus tôt, et à notre petit schéma.</p>
<p>Dans une Command, on agit sur le Model.</p>
<p>Si on relis DiaporamaContext, on a une <strong>StartupCommand</strong>, dispatchée au lancement de l’application : </p>
<p>[as3]<br />
package controller.commands<br />
{<br />
	import model.DiaporamaData;</p>
<p>	import org.robotlegs.mvcs.Command;</p>
<p>	public class StartupCommand extends Command<br />
	{<br />
		[Inject]<br />
		public var diaporamaData : DiaporamaData;</p>
<p>		override public function execute() : void<br />
		{<br />
			diaporamaData.load();<br />
		}</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<p>Enfin on a une <strong>ConfigCompleteCommand</strong>, qui est appelée à la fin du chargement du XML. Pour l’instant, nous n’avons rien de visible sur la scène, notre appli est vide!</p>
<p>[as3]<br />
package controller.commands<br />
{<br />
	import controller.events.DiaporamaEvent;<br />
	import view.components.DiaporamaDisplay;<br />
	import view.components.DiaporamaControls;</p>
<p>	import org.robotlegs.mvcs.Command;</p>
<p>	/**<br />
	 * appelée à la fin du chargement du .XML<br />
	 */<br />
	public class ConfigCompleteCommand extends Command<br />
	{<br />
		// on récupére l&#8217;Event déclencheur<br />
		[Inject]<br />
		public var event : DiaporamaEvent;</p>
<p>		override public function execute() : void<br />
		{<br />
			// on crée la vue<br />
			contextView.addChild( new DiaporamaDisplay() );<br />
			contextView.addChild( new DiaporamaControls() );</p>
<p>			// on force le refresh de la vue, pour afficher la première image<br />
			dispatch( new DiaporamaEvent( DiaporamaEvent.REFRESH, false, false, event.entry ) );<br />
		}</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<p>C’est donc là qu’est créé la couche visible de notre application! On notera 2 choses très importantes sur cette Command :</p>
<ul>
<li><strong>l’injection d’un event :</strong> en effet, une Command on l’a compris est une sorte de gros handler d’événement. Pour récupérer les paramètres de l’Event déclencheur (ici <em>entry</em> nous intéresse), on injecte l’event.</li>
<li><strong>l’appel à contextView :</strong> pour récupérer une référence vers la Stage et ajouter des éléments à la DisplayList, RobotLegs fourni une référence nommée <em>contextView</em> vers la variable du même nom déclarée dans <strong>DiaporamaContext</strong>.</li>
</ul>
<p><strong>Note :</strong><em>plutôt qu’un appel à contextView, dans certains cas (menu clic-droit custom, resize de stage, curseur custom, multi-fenêtre sous AIR, etc), il est indiqué de créer un StageMediator.</em></p>
<h4>Aller plus loin</h4>
<p>Sur ce tutoriel en particulier, les choses suivantes peuvent être effectuées pour s&#8217;entraîner :</p>
<ul>
<li>séparer correctement <strong>DiaporamaEvent</strong> en plusieurs événements disctincts.</li>
<li>ajouter un <strong>StageMediator</strong> et customiser le menu du clic-droit</li>
<li>passer l&#8217;adresse du XML en FlashVars</li>
<li>ajouter du son : un indice, un son est une View.</li>
</ul>
<p>Sur un plan plus général :</p>
<ul>
<li>Beaucoup d&#8217;exemples ici : <a title="Exemples robotlegs" href="http://www.robotlegs.org/examples/" target="_blank">http://www.robotlegs.org/examples/</a></li>
<li>RobotLegs a le site de support le mieux fichu qui soit : <a title="support robotlegs" href="http://knowledge.robotlegs.org/" target="_blank">http://knowledge.robotlegs.org/</a></li>
<li>Les best-practices sont ici : <a title="best practices robotlegs" href="http://github.com/robotlegs/robotlegs-framework/wiki/Best-Practices" target="_blank">http://github.com/robotlegs/robotlegs-framework/wiki/Best-Practices</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-55/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Découverte du framework Robotlegs [4/5]</title>
		<link>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-45/</link>
		<comments>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-45/#comments</comments>
		<pubDate>Fri, 12 Nov 2010 17:42:49 +0000</pubDate>
		<dc:creator>fh</dc:creator>
				<category><![CDATA[Actionscript 3]]></category>
		<category><![CDATA[Flash Platform]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[actionscript]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[robotlegs]]></category>

		<guid isPermaLink="false">http://www.entremidietdeux.com/?p=107</guid>
		<description><![CDATA[La couche View Les composants visuels Les composants visuels doivent être réutilisables de projet à projet. Ils sont donc agnostiques au projet, et ne doivent contenir aucune référence au framework. On doit littéralement pouvoir les importer dans un projet vide, &#8230; <a href="http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-45/">Continuer la lecture <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<h3>La couche View</h3>
<h4>Les composants visuels</h4>
<p><img class="alignright size-full wp-image-82" title="rl-flag-sm" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/rl-flag-sm.png" alt="" width="69" height="90" /><br />
Les composants visuels doivent être réutilisables de projet à projet. Ils sont donc agnostiques au projet, et ne doivent contenir aucune référence au framework. On doit littéralement pouvoir les importer dans un projet vide, et les lancer seuls &#8211; ils sont autonomes.</p>
<p>Pour ce projet nous aurons deux composants visuels :<br />
<span id="more-107"></span></p>
<ul>
<li><strong>DiaporamaDisplay :</strong> c’est un UILoader en plus léger et plus classe. On lui passe une URL et il charge cette image puis l’affiche. Et c’est tout.</li>
</ul>
<p>[as3]<br />
package view.components<br />
{<br />
	import model.vo.Entry;<br />
	import controller.events.DiaporamaEvent;</p>
<p>	import com.gskinner.motion.GTween;<br />
	import com.gskinner.motion.easing.Quadratic;	</p>
<p>	import flash.display.Sprite;<br />
	import flash.events.Event;<br />
	import flash.display.Loader;<br />
	import flash.display.Bitmap;<br />
	import flash.net.URLRequest;<br />
	import flash.display.LoaderInfo;</p>
<p>	/**<br />
	 * composant visuel basique,<br />
	 * un afficheur d&#8217;image simple avec une transition animée<br />
	 */<br />
	public class DiaporamaDisplay extends Sprite<br />
	{<br />
		private var _front : Sprite;<br />
		private var _back : Sprite;</p>
<p>		private var _backContent : Bitmap;<br />
		private var _frontContent : Bitmap;</p>
<p>		private var _current : Sprite;</p>
<p>		public function DiaporamaDisplay()<br />
		{<br />
			_back = new Sprite();<br />
			_front = new Sprite();</p>
<p>			addChild(_back);<br />
			addChild(_front);<br />
		}</p>
<p>		public function refresh( entry : Entry ) : void<br />
		{<br />
			var l : Loader = new Loader();<br />
			l.contentLoaderInfo.addEventListener( Event.COMPLETE, onLoadComplete );<br />
			l.load( new URLRequest(entry.file) );<br />
		}</p>
<p>		private function onLoadComplete( e : Event ) : void<br />
		{<br />
			_backContent = (e.target as LoaderInfo).loader.content as Bitmap;<br />
			_back.alpha = 0;<br />
			_backContent.addEventListener( Event.ADDED_TO_STAGE, onAdded );<br />
			_back.addChild(_backContent);<br />
		}</p>
<p>		private function onAdded(e:Event):void<br />
		{<br />
			removeEventListener(Event.ADDED_TO_STAGE, onAdded);<br />
			new GTween( _back, .5, { alpha: 1 }, { onComplete: onFadeComplete, ease : Quadratic.easeOut } );<br />
			new GTween( _front, .5, { alpha: 0 }, { ease : Quadratic.easeIn } );<br />
		}</p>
<p>		private function onFadeComplete( g : GTween ) : void<br />
		{<br />
			var frontBuffer : Sprite = _front;<br />
			_front = _back;<br />
			_back = frontBuffer;</p>
<p>			dispatchEvent( new DiaporamaEvent( DiaporamaEvent.LOAD_COMPLETE ) );<br />
		}</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<ul>
<li><strong>DiaporamaControls :</strong> constitué d’une flêche vers la droite (<em>btNext</em>) et d’une flèche vers la gauche (<em>btPrevious</em>). il n’a que 2 méthodes, <em>enable()</em> et <em>disable()</em>, pour freezer les boutons, par exemple le temps d’un chargement.</li>
</ul>
<p>[as3]<br />
package view.components<br />
{<br />
	import controller.events.DiaporamaEvent;</p>
<p>	import com.gskinner.motion.GTween;</p>
<p>	import flash.display.MovieClip;<br />
	import flash.events.MouseEvent;</p>
<p>	/**<br />
	 * composant visuel basique, 2 flêches que l&#8217;on peut &laquo;&nbsp;geler&nbsp;&raquo;<br />
	 * par exemple le temps d&#8217;un chargement<br />
	 */<br />
	public class DiaporamaControls extends DiaporamaControls_skin<br />
	{</p>
<p>		public function DiaporamaControls()<br />
		{<br />
			disable();<br />
		}</p>
<p>		/**<br />
		 * rend actif les flêches<br />
		 */<br />
		public function enable() : void<br />
		{<br />
			for each( var button : MovieClip in [ btPrevious, btNext ] ) {<br />
				if ( button.alpha != 1 )<br />
					new GTween( button, .5, { alpha : 1 } );<br />
				button.addEventListener( MouseEvent.CLICK, onClick );<br />
				button.buttonMode = true;<br />
			}<br />
		}</p>
<p>		/**<br />
		 * rend inactif les flêches<br />
		 */<br />
		public function disable() : void<br />
		{<br />
			for each( var button : MovieClip in [ btPrevious, btNext ] ) {<br />
				if ( button.alpha != .5 )<br />
					new GTween( button, .5, { alpha : 0 } );<br />
				button.removeEventListener( MouseEvent.CLICK, onClick );<br />
				button.buttonMode = false;<br />
			}<br />
		}</p>
<p>		/**<br />
		 * émission d&#8217;un Event simple au click<br />
		 *<br />
		 * @param	e<br />
		 */<br />
		private function onClick( e : MouseEvent ) : void<br />
		{<br />
			dispatchEvent( new DiaporamaEvent(<br />
				{<br />
					btNext : DiaporamaEvent.NEXT,<br />
					btPrevious : DiaporamaEvent.PREVIOUS<br />
				}[e.currentTarget.name]<br />
			) );<br />
		}</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<p>Nous avons bien 2 composants agnostiques et autonomes, sans aucune référence à un quelconque objet issu de l’application. Pour être parfait, nous aurions dû utiliser un Event séparé par composant, et faire un .swc par composant + événement attitré.</p>
<h3>Les Mediators</h3>
<p>Le rôle des Mediators est :</p>
<ul>
<li>d’écouter l’application et agir sur leur composant,</li>
<li>d’écouter leur composant et d’en informer l’application.</li>
</ul>
<p>Commençons par <strong>DisplayControlsMediator</strong>, qui va s’occuper de <strong>DisplayControls:</strong></p>
<p>[as3]<br />
package view.mediators<br />
{<br />
	import controller.events.DiaporamaEvent;<br />
	import view.components.DiaporamaControls;</p>
<p>	import org.robotlegs.mvcs.Mediator;</p>
<p>	/**<br />
	 * Agit sur diaporamaDisplay en fonction des événements reçus<br />
	 * du composant ou de l&#8217;application<br />
	 *<br />
	 */<br />
	public class DiaporamaControlsMediator extends Mediator<br />
	{<br />
		[Inject]<br />
		public var controls : DiaporamaControls;</p>
<p>		override public function onRegister() : void<br />
		{<br />
			// événements issus de l&#8217;application<br />
			eventMap.mapListener( eventDispatcher, DiaporamaEvent.LOAD_COMPLETE, onLoadComplete );</p>
<p>			// événements issus du composant<br />
			controls.addEventListener( DiaporamaEvent.NEXT, onClick );<br />
			controls.addEventListener( DiaporamaEvent.PREVIOUS, onClick );<br />
		}</p>
<p>		private function onClick( e : DiaporamaEvent ) : void<br />
		{<br />
			controls.disable();<br />
			dispatch(e);<br />
		}</p>
<p>		private function onLoadComplete( e : DiaporamaEvent ) : void<br />
		{<br />
			controls.enable();<br />
		}</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<p>On constate que son rôle est d’appeler les méthodes <em>enable()</em> et <em>disable()</em> au bon moment, et forwarder son événement de clic, tout simplement.</p>
<p>Occupons-nous de <strong>DiaporamaDisplayMediator</strong>, qui s’occupe de <strong>DiaporamaDisplay</strong>:</p>
<p>[as3]<br />
package view.mediators<br />
{<br />
	import controller.events.DiaporamaEvent;<br />
	import view.components.DiaporamaDisplay;</p>
<p>	import org.robotlegs.mvcs.Mediator;</p>
<p>	/**<br />
	 * Agit sur diaporamaDisplay en fonction des événements reçus<br />
	 * du composant ou de l&#8217;application<br />
	 *<br />
	 */</p>
<p>	public class DiaporamaDisplayMediator extends Mediator<br />
	{<br />
		[Inject]<br />
		public var display : DiaporamaDisplay;</p>
<p>		override public function onRegister() : void<br />
		{<br />
			// événements issus de l&#8217;application<br />
			eventMap.mapListener( eventDispatcher, DiaporamaEvent.REFRESH, onRefresh );</p>
<p>			// événements issus du composant<br />
			display.addEventListener( DiaporamaEvent.LOAD_COMPLETE, onLoadComplete );<br />
		}</p>
<p>		private function onRefresh( e : DiaporamaEvent ) : void<br />
		{<br />
			display.refresh(e.entry);<br />
		}</p>
<p>		private function onLoadComplete( e : DiaporamaEvent ) : void<br />
		{<br />
			dispatch(e);<br />
		}</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<p>On constate que son rôle est d’appeler la méthode <em>refresh()</em> au bon moment, et forwarder son événement de fin de chargement.</p>
<p>On remarque un <em>DiaporamaEvent.REFRESH</em>, qui n’est pas mappé sur une Command dans <strong>DiaporamaContext</strong>. En effet, seul ce Mediator est concerné par cet événement, il n’est pas obligatoire d’utiliser une Command.</p>
<p>L’utilité et le rôle d’un Mediator est souvent difficile à cerner pour un débutant. A la relecture de l’integralité du code que nous avons tapé pour l’instant, nous constatons que “<em>c’est là que se font les addEventListener sur la Vue, et les appels de méthodes</em>”. C’est aussi simple que ça.</p>
<p><strong>Note :</strong> <em>on voit que l’injecteur est utilisé pour injecter le composant. C’est une très mauvaise idée d’utiliser cet injecteur pour injecter directement dans le Mediator un Model. Cela nuit au découplage en couches. Si un Mediator a besoin d’une donnée, cette donnée doit lui parvenir via un Event reçu, et c’est tout.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-45/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Découverte du framework Robotlegs [3/5]</title>
		<link>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-35/</link>
		<comments>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-35/#comments</comments>
		<pubDate>Fri, 12 Nov 2010 17:27:39 +0000</pubDate>
		<dc:creator>fh</dc:creator>
				<category><![CDATA[Actionscript 3]]></category>
		<category><![CDATA[Flash Platform]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[actionscript]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[robotlegs]]></category>

		<guid isPermaLink="false">http://www.entremidietdeux.com/?p=100</guid>
		<description><![CDATA[La couche Model Commençons le développement par la couche la plus basse et la plus basse, la couche de Model, qui charge les données et les stocke pour leur usage au sein de l’application. Voici le XML de contenu : &#8230; <a href="http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-35/">Continuer la lecture <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<h3>La couche Model</h3>
<p><a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/rl-flag-sm.png"><img src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/rl-flag-sm.png" alt="" title="rl-flag-sm" width="69" height="90" class="alignright size-full wp-image-82" /></a><br />
Commençons le développement par la couche la plus basse et la plus basse, la couche de Model, qui charge les données et les stocke pour leur usage au sein de l’application. Voici le XML de contenu :</p>
<p>[xml]<br />
<?xml version="1.0" encoding="utf-8" ?><br />
<data><br />
	<entry file="img/placeholder-00.png"></p>
<p>	</entry><br />
	<entry file="img/placeholder-01.png"></p>
<p>	</entry><br />
	<entry file="img/placeholder-02.png"></p>
<p>	</entry><br />
</data><br />
[/xml]<br />
<span id="more-100"></span><br />
On a un seul type de données à stocker dans une structure séquentielle de type liste. Nous allons stocker chaque entrée dans un type personnalisé correspondant à cette structure. C’est un pattern de Value Object, nommons ce type le type <strong>Entry</strong>.</p>
<p>[as3]<br />
package model.vo<br />
{<br />
	public class Entry<br />
	{</p>
<p>		public var file : String;<br />
		public var title : String;</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<p>Construisons maintenant la classe <strong>DiaporamaData</strong>, dont le rôle sera :</p>
<ul>
<li><strong>charger un fichier XML,</strong></li>
<li><strong>stocker les données :</strong> ici dans un <em>Vector</em>.</li>
<li><strong>passer à l’image suivante :</strong> dispatcher le nom du fichier de l’image suivante à charger, et cycler à l’infini</li>
<li><strong>passer à l’image précédente :</strong> dispatcher le nom du fichier de l’image précédente à charger, et cycler à l’infini</li>
</ul>
<p>[as3]<br />
package model<br />
{<br />
	import controller.events.DiaporamaEvent;<br />
	import model.vo.Entry;	</p>
<p>	import org.robotlegs.mvcs.Actor;</p>
<p>	import flash.net.URLLoader;<br />
	import flash.net.URLRequest;<br />
	import flash.events.Event;<br />
	import flash.events.IOErrorEvent;	</p>
<p>	public class DiaporamaData extends Actor<br />
	{</p>
<p>		private var _entries : Vector.<Entry>;<br />
		private var _current : uint = 0;</p>
<p>		public function DiaporamaData()<br />
		{<br />
			super();<br />
		}</p>
<p>		public function load( url : String = &#8216;diaporama-content.xml&#8217; ) : void<br />
		{<br />
			var content : URLLoader = new URLLoader();<br />
			content.addEventListener( Event.COMPLETE, onComplete );<br />
			content.addEventListener( IOErrorEvent.IO_ERROR, onIOError );<br />
			content.load( new URLRequest(url) );<br />
		}</p>
<p>		private function onComplete( e : Event ) : void<br />
		{<br />
			_entries = new Vector.<Entry>();</p>
<p>			var content : XML = new XML((e.target as URLLoader).data);<br />
			for each( var entryData : XML in content.entry ) {<br />
				var entry : Entry = new Entry();<br />
				entry.file = entryData.@file;<br />
				entry.title = entryData.title;<br />
				_entries.push(entry);<br />
			}</p>
<p>			eventDispatcher.dispatchEvent( new DiaporamaEvent( DiaporamaEvent.CONFIG_COMPLETE, false, false, _entries[_current] ) );<br />
		}</p>
<p>		public function next() : void<br />
		{<br />
			if ( _current + 1 < _entries.length ) {<br />
				_current++;<br />
			} else {<br />
				_current = 0;<br />
			}</p>
<p>			eventDispatcher.dispatchEvent( new DiaporamaEvent( DiaporamaEvent.REFRESH, false, false, _entries[_current] ) );<br />
		}</p>
<p>		public function previous() : void<br />
		{<br />
			if ( _current - 1 >= 0 ) {<br />
				_current&#8211;;<br />
			} else {<br />
				_current = _entries.length &#8211; 1;<br />
			}</p>
<p>			eventDispatcher.dispatchEvent( new DiaporamaEvent( DiaporamaEvent.REFRESH, false, false, _entries[_current] ) );<br />
		}</p>
<p>		private function onIOError( e : IOErrorEvent ) : void<br />
		{<br />
			throw new Error( &#8216;content could not be loaded&#8217; );<br />
		}</p>
<p>		public function get entries() : Vector.<Entry><br />
		{<br />
			return _entries;<br />
		}</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<p>C’est là tout ce dont nous avons besoin pour cette application. Le Model est généralement ce qu’il y a de plus facile : on expose quelques méthodes publiques, on émet simplement un/des événements selon l’état du Model.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-35/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Découverte du framework Robotlegs [2/5]</title>
		<link>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-25/</link>
		<comments>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-25/#comments</comments>
		<pubDate>Fri, 12 Nov 2010 13:20:53 +0000</pubDate>
		<dc:creator>fh</dc:creator>
				<category><![CDATA[Actionscript 3]]></category>
		<category><![CDATA[Flash Platform]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[actionscript]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[robotlegs]]></category>

		<guid isPermaLink="false">http://www.entremidietdeux.com/?p=87</guid>
		<description><![CDATA[Diaporama sous Robotlegs : la structure générale Robotlegs est disponible sous forme d’un .zip contenant sources, .swc et documentation, à cette adresse : http://www.robotlegs.org/ Commençons par créer un projet AS3 sous FlashDevelop, puis créer la structure de packages que nous &#8230; <a href="http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-25/">Continuer la lecture <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<h3>Diaporama sous Robotlegs : la structure générale</h3>
<p><img class="alignright size-full wp-image-82" title="rl-flag-sm" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/rl-flag-sm.png" alt="" width="69" height="90" /><br />
Robotlegs est disponible sous forme d’un .zip contenant sources, .swc et documentation, à cette adresse : <a title="Robotlegs" href="http://www.robotlegs.com" target="_blank">http://www.robotlegs.org/</a></p>
<p>Commençons par créer un projet AS3 sous FlashDevelop, puis créer la structure de packages que nous allons remplir :</p>
<p><a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/illu-project-file-structure-1.png"><img class="aligncenter size-full wp-image-90" title="illu-project-file-structure (1)" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/illu-project-file-structure-1.png" alt="" width="260" height="258" /></a></p>
<p>On voit bien les 3 couches à développer :</p>
<ul>
<li><strong>controller </strong>: il contient les différentes Command qui implémentent cette couche. Un package séparé pour les events qui sont parfois nombreux.</li>
<li><strong>model : </strong>contient les différents Actors, un package est prévu pour les V.O. (“Value Objects”) qui sont des objets de données simples.</li>
<li><strong>view : </strong>un sous-package pour les Mediators, un autre pour les classes graphiques &#8211; on a vu plus haut qu’une classe graphique communique toujours par un Mediator.</li>
</ul>
<p><span id="more-87"></span></p>
<h3>le Context, coeur de l&#8217;application</h3>
<p>La structure globale d’une application Robotlegs est définié dans une classe dite “contexte”, qui étend <em>org.robotlegs.mvcs.Context</em>. Cette classe est la façade de l’application, c’est là que sont définis les différents paramètres de l’application.</p>
<p>Au runtime, une application Robotlegs démarre de la manière suivante :</p>
<ul>
<li>point d’entrée</li>
<li>instantiation du Context</li>
<li>appel de la méthode startup() du Context.</li>
</ul>
<p>La première classe de notre application sera son point d’entrée de compilation, nommé <strong>Diaporama</strong> :</p>
<p>[as3]<br />
package<br />
{<br />
	import flash.display.Sprite;</p>
<p>	/**<br />
	 * point d&#8217;entrée de l&#8217;application<br />
	 *<br />
	 */<br />
	public class Diaporama extends Sprite<br />
	{<br />
		private var c : DiaporamaContext; </p>
<p>		public function Diaporama() : void<br />
		{<br />
			c = new DiaporamaContext(this);<br />
		}</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<p>Jusque-là c’est plutôt simple, on instantie <strong>DiaporamaContext</strong> en lui passant l’objet graphique racine de l’application, en conservant une référence explicite vers l&#8217;applicationContext pour éviter d&#8217;être supprimé par le ramasse-miette. Voyons maintenant <strong>DiaporamaContext</strong> :</p>
<p>[as3]<br />
package<br />
{<br />
	import org.robotlegs.mvcs.Context;</p>
<p>	import flash.display.DisplayObjectContainer;</p>
<p>	/**<br />
	 * Contexte de l&#8217;application Diaporama<br />
	 *<br />
	 */<br />
	public class DiaporamaContext extends Context<br />
	{</p>
<p>		public function DiaporamaContext( 	contextView : DisplayObjectContainer,<br />
							autoStartup : Boolean = true )<br />
		{<br />
			// passage explicite des arguments à la classe mère<br />
                        super(contextView, autoStartup);<br />
		}</p>
<p>		override public function startup() : void<br />
		{</p>
<p>		}</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<p><strong>DiaporamaContext</strong> étend <strong>Context</strong>. il reçoit bien en argument l’objet graphique racine. Il passe cette objet à sa classe mère, puis la méthode<em> startup()</em> est appelée automatiquement puisque nous passons <em>autoStartup = true</em> à la classe mère.</p>
<p>La méthode<em> startup()</em> est vide pour l’instant. C’est là que toute la configuration de l’application va se faire. Commençons par définir notre Controller.</p>
<h3>définition du Controller</h3>
<p>C’est un diaporama classique, avec 2 boutons “précédent” et “suivant”, une barre de progression du chargement par image, et un XML de contenu.</p>
<p>Commençons par lister l’intégralité des événements générant un changement d’état lors du cycle de vie de l’application :</p>
<ul>
<li><strong>DiaporamaEvent.CONFIG_COMPLETE : </strong>on charge un XML, on aura donc un événement de fin de chargement.</li>
<li><strong>DiaporamaEvent.NEXT :</strong> on a la possibilité d’un clic sur ce bouton.</li>
<li><strong>DiaporamaEvent.PREVIOUS :</strong> on a la possibilité d’un clic sur ce bouton.</li>
</ul>
<p>Comme indiqué plus haut, les Events d’implémentation Flash classiques n’ont pas leur place dans le contrôleur s’ils ne concernent pas toute l’application. On notera l’absence des événements du type <em>LOAD_START, LOAD_PROGRESS, LOAD_COMPLETE</em>. Ceux-là ne concernant que le composant d’affichage, ils y restent.</p>
<p>Nous allons mapper comme il se doit chacun de ces événements vers une Command :</p>
<p>[as3]<br />
override public function startup() : void<br />
{<br />
	// définition du Controller<br />
	commandMap.mapEvent( DiaporamaEvent.STARTUP, StartupCommand );<br />
	commandMap.mapEvent( DiaporamaEvent.CONFIG_COMPLETE, ConfigCompleteCommand );<br />
	commandMap.mapEvent( DiaporamaEvent.NEXT, NextCommand );<br />
	commandMap.mapEvent( DiaporamaEvent.PREVIOUS, PreviousCommand );<br />
}<br />
[/as3]</p>
<p>C’est un peu l’équivalent d’un //addEventListener//, sauf qu’au lieu d’appeler une fonction, on appelle la classe Command adaptée, plus particulièrement sa méthode <em>execute()</em>.</p>
<h3>définition du Model</h3>
<p>Pour cette application nous n’avons qu’un seul type de données à charger, en une seule fois au début du cycle de vie, c’est le XML de configuration.</p>
<p>[xml]<br />
<data><br />
	<entry file="img/placeholder-00.png"></p>
<p>	</entry><br />
	<entry file="img/placeholder-01.png"></p>
<p>	</entry><br />
	<entry file="img/placeholder-02.png"></p>
<p>	</entry><br />
</data><br />
[/xml]</p>
<p>Nous aurons donc une seule classe dans notre Model, appelons-là par exemple <strong>DiaporamaData</strong>. Modifions <strong>DiaporamaContext</strong> pour en informer le framework :</p>
<p>[as3]<br />
override public function startup() : void<br />
{<br />
	// définition du Controller<br />
	commandMap.mapEvent( DiaporamaEvent.STARTUP, StartupCommand );<br />
	commandMap.mapEvent( DiaporamaEvent.CONFIG_COMPLETE, ConfigCompleteCommand );<br />
	commandMap.mapEvent( DiaporamaEvent.NEXT, NextCommand );<br />
	commandMap.mapEvent( DiaporamaEvent.PREVIOUS, PreviousCommand );</p>
<p>	// définition du Model<br />
	injector.mapSingleton( DiaporamaData );<br />
}<br />
[/as3]</p>
<p>Nous indiquons simplement à l’injecteur que lorsque nous demanderons l’accès à un objet <strong>DiaporamaData</strong>.</p>
<h3>Définition de la View</h3>
<p>Pour cette application nous n’avons que 2 composants visuels à l’écran :</p>
<ul>
<li>DiaporamaDisplay : C’est simplement le container destiné à afficher l’image chargée. Concrétement, c’est un MovieClip.</li>
<li>DiaporamaControl : C’est simplement le container des 2 boutons “précédent” et “suivant”.</li>
</ul>
<p>Modifions <strong>DiaporamaContext</strong> pour en informer le framework :</p>
<p>[as3]<br />
override public function startup() : void<br />
{<br />
	// définition du Controller<br />
	commandMap.mapEvent( DiaporamaEvent.STARTUP, StartupCommand );<br />
	commandMap.mapEvent( DiaporamaEvent.CONFIG_COMPLETE, ConfigCompleteCommand );<br />
	commandMap.mapEvent( DiaporamaEvent.NEXT, NextCommand );<br />
	commandMap.mapEvent( DiaporamaEvent.PREVIOUS, PreviousCommand );</p>
<p>	// définition du Model<br />
    injector.mapSingleton( DiaporamaData );</p>
<p>	// définition de la View<br />
	mediatorMap.mapView( DiaporamaDisplay, DiaporamaDisplayMediator );<br />
	mediatorMap.mapView( DiaporamaControls, DiaporamaControlsMediator );<br />
}<br />
[/as3]</p>
<p>On a vu plus haut q’un composant = un Mediator. Ici nous mappons simplement un composant vers son Mediator. Robotlegs écoute l’événement <em>ADDED_TO_STAGE</em> en permanence, quand il détectera l’ajout d’un composant <strong>DiaporamaDisplay</strong>, il créera automatiquement son pendant <strong>DiaporamaDisplayMediator</strong>.</p>
<h3>Récapitulatif</h3>
<p>[as3]<br />
package<br />
{<br />
	import controller.events.*;<br />
	import controller.commands.*;<br />
	import view.mediators.*;<br />
	import view.components.*;<br />
	import model.*;</p>
<p>	import org.robotlegs.mvcs.Context;</p>
<p>	import flash.display.DisplayObjectContainer;</p>
<p>	/**<br />
	 * Contexte de l&#8217;application Diaporama<br />
	 *<br />
	 */<br />
	public class DiaporamaContext extends Context<br />
	{</p>
<p>		public function DiaporamaContext( contextView : DisplayObjectContainer,<br />
						  autoStartup : Boolean = true )<br />
		{<br />
			super(contextView, autoStartup);<br />
		}</p>
<p>		override public function startup() : void<br />
		{<br />
			// définition du Controller<br />
			commandMap.mapEvent( DiaporamaEvent.STARTUP, StartupCommand );<br />
			commandMap.mapEvent( DiaporamaEvent.CONFIG_COMPLETE, ConfigCompleteCommand );<br />
			commandMap.mapEvent( DiaporamaEvent.NEXT, NextCommand );<br />
			commandMap.mapEvent( DiaporamaEvent.PREVIOUS, PreviousCommand );</p>
<p>			// définition du Model<br />
			injector.mapSingleton( DiaporamaData );</p>
<p>			// définition de la View<br />
			mediatorMap.mapView( DiaporamaDisplay, DiaporamaDisplayMediator );<br />
			mediatorMap.mapView( DiaporamaControls, DiaporamaControlsMediator );</p>
<p>			// démarrage de l&#8217;application<br />
			dispatchEvent( new DiaporamaEvent( DiaporamaEvent.STARTUP ) );<br />
		}</p>
<p>	}</p>
<p>}<br />
[/as3]</p>
<p>Voilà la classe <strong>DiaporamaContext</strong> qui définit l’intégralité des acteurs en jeu dans notre application, ainsi que leurs relations. On constate que c’est très simple d’ajouter des fonctionnalités et des états complémentaires à cette application, voire un chargement d’autres données, une nouvelle vue, etc.</p>
<p>On y a ajouté une dernière ligne qui dispatche l’événement de départ <em>STARTUP</em>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-25/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Découverte du framework Robotlegs [1/5]</title>
		<link>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-15/</link>
		<comments>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-15/#comments</comments>
		<pubDate>Thu, 11 Nov 2010 23:45:45 +0000</pubDate>
		<dc:creator>fh</dc:creator>
				<category><![CDATA[Actionscript 3]]></category>
		<category><![CDATA[Flash Platform]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[robotlegs]]></category>

		<guid isPermaLink="false">http://www.entremidietdeux.com/?p=80</guid>
		<description><![CDATA[Ce tutoriel présente les grandes lignes de la micro-architecture Robotlegs, un projet open-source de framework architectural de plus en plus répandu. Il est destiné aux débutants, et a été spécialement conçu et rédigé pour être lu entre midi et deux &#8230; <a href="http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-15/">Continuer la lecture <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<h4><a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/rl-flag-sm.png"><img class="alignright size-full wp-image-82" title="rl-flag-sm" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/rl-flag-sm.png" alt="" width="69" height="90" /></a>Ce tutoriel présente les grandes lignes de la micro-architecture <em>Robotlegs</em>, un projet open-source de framework architectural de plus en plus répandu. Il est destiné aux débutants, et a été spécialement conçu et rédigé pour être lu entre midi et deux dans un open-space bruyant. Vous n&#8217;avez plus d&#8217;excuses.</h4>
<h3>Retour sur les fondamentaux de l’architecture logicielle orienté objet.</h3>
<p>Les applications Flash ou Flex sont constituées d’objets, qui communiquent entre eux par des événements. Le paradigme orienté-objet existant depuis les temps immémoriaux du langage Smalltalk, certaines structures de classes récurrentes répondant à des problèmes précis ont émergées au fil des années. Ces structures récurrentes ont été listées dans l’ouvrage <em>“Design Patterns”</em> du GoF, en voici quelques-uns biens connus :</p>
<ul>
<li><strong>Factory :</strong> encapsule la création d’un objet complexe de classe A dans une autre classe B.</li>
<li><strong>Adapter :</strong> encapsule un objet A dotée d’une interface inadaptée au projet dans une classe B.</li>
<li><strong>Command :</strong> encapsule une méthode dans une classe indépendante A, dont le rôle est d’agir sur des classes B,C,D.</li>
</ul>
<p>Les design-patterns ne sont pas un code-source unique et précis. Ils sont indépendants du langage, un design-pattern est une <em>pratique</em>. Il y a autant d’implémentation que de langages de programmation, et plusieurs implémentations possibles au sein d’un même langage.</p>
<p>On trouve des <em>design-patterns simples</em>, répondant à une tâche précise (Factory, Adapter; Command, &#8230;) et des <em>design-patterns composés</em>, répondant à des problèmes plus vastes et plus complexes en réunissant plusieurs patterns. Par exemple, pour structurer une application qui affiche des données via une <a title="IHM" href="http://fr.wikipedia.org/wiki/Interface_homme-machine" target="_blank">IHM</a> permettant de filtrer/modifier les données, on utilise le plus souvent le pattern <strong>Model-View-Controller</strong> (dit “MVC”).</p>
<p>Dans le cas précis du développement Flash, ce dernier type d’applications est évidemment le plus courant, la nature même de Flash étant le développement de RIA. Pour structurer ce type d’applications, plusieurs angles d’attaques existent :</p>
<ul>
<li><strong>ne pas utiliser de MVC :</strong> pour une petite application, on code tout rapidement dans une classe.</li>
<li><strong>le framework “maison” :</strong> on produit une implémentation personnelle de MVC, qu’on réutilise ensuite.</li>
<li><strong>le framework Cairngorm :</strong> développé par Adobe, c’est l’implémentation de référence censée être utilisée et connue, en particulier par tous les développeurs Flex.</li>
<li><strong>les frameworks MVC alternatifs :</strong> issus de la communauté, les plus courants sont Robotlegs, PureMVC, Parsley.</li>
<li><strong>les frameworks non-MVC alternatifs :</strong> basés sur d’autres patterns, comme Swiz et Mate.</li>
</ul>
<p>Ce tutoriel aborde Robotlegs. Ses points forts par rapport aux autres frameworks sont son utilisation astucieuse de l’injection de dépendances, sa simplicité d’utilisation et sa légèreté (c’est une micro-architecture plus qu’un framework).<br />
<span id="more-80"></span></p>
<h3>Le MVC et son implémentation par Robotlegs</h3>
<h4>Vue générale</h4>
<p>Attardons-nous maintenant sur le pattern MVC, et son implémentation particulière au sein de Robotlegs. MVC scinde l’application en 3 couches :</p>
<ul>
<li><strong>la couche Model :</strong> regroupe l’ensemble des classes chargeant et stockant les données. Une classe du Model émet des événements en direction des autres couches, et est pilotée par des méthodes publiques.</li>
<li><strong>la couche Controller :</strong> écoute la couche Model et la couche View, et agit sur l’un ou l’autre en appelant des méthodes publiques, en fonction des actions de l’utilisateur, ou de l’évolution des données.</li>
<li><strong>la couche View :</strong> affiche les données du Model, et émet des événements lors des actions utilisateurs.</li>
</ul>
<p><a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/mvc-schema-theorique.png"><img class="aligncenter size-full wp-image-81" title="mvc-schema-theorique" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/mvc-schema-theorique.png" alt="" width="341" height="278" /></a></p>
<p>Ce schéma donne une vue globale de l’implémentation du MVC par Robotlegs.<br />
On remarque deux types de flèches bien distincts :</p>
<ul>
<li><strong>les flèches pointillées :</strong> ce sont des événements.</li>
<li><strong>les flèches pleines :</strong> ce sont des appels de méthodes publiques &#8211; par exemple : <em>applicationModel.load(‘content.xml’);</em> ou <em>applicationView.refresh();</em></li>
</ul>
<p>Si le Controller est capable de passer ce type d’appel, on en déduit que le Controller possède une référence vers les classes de la View, et les classes du Model. Or, c’est assez négatif, puisque cela sous-entend d’avoir “en dur” dans le Controller des variables pointant vers toutes les instances existantes des classes du Model et de la View. Ce qui constituerait une “dépendance” du Controller envers la View et le Model. Comment supprimer cette dépendance ?</p>
<h4>L’injection de dépendance</h4>
<p>Robotlegs, et c’est là son principal point positif, utilise <strong>“l’injection de dépendance”</strong>. Lorsqu’un appel d’une méthode publique de la View et du Model est nécessaire, la dépendance est injectée dans la partie du Controller qui en a besoin. Elle n’existe que le temps nécessaire, puis disparaît.</p>
<p>Au niveau code, cela se présente ainsi, au sein du Controller ici :</p>
<p>[as3]<br />
public class LoadConfigurationCommand extends Command<br />
{<br />
	// l&#8217;injection de dépendances a lieu ici<br />
        [Inject]<br />
	public var applicationData : ApplicationData;</p>
<p>	override public function execute() : void<br />
	{<br />
		// applicationData est peuplée! pourtant elle est simplement déclarée plus haut<br />
		// c&#8217;est l&#8217;injecteur qui a injecté cette classe !<br />
		applicationData.load(&#8216;swf/slideshow-conf.xml&#8217;);<br />
	}</p>
<p>}<br />
[/as3]</p>
<p>On note l’usage très astucieux du meta-tag<em> [Inject].</em> En effet, il est tout à fait possible pour un développeur de créer de nouveaux meta-tags. Et ce meta-tag particulier à Robotlegs va en fait déclencher en arrière-plan l’injection de la dépendance. La variable publique va automatiquement être peuplée par Robotlegs.</p>
<p>Derrière ce tag custom se cache en réalité toute une couche d’injection dénommée <strong>SwiftSuspenders</strong>, qui est indépendante de Robotlegs et peut parfaitement être utilisée sur des projets non-Robotlegs, il suffit d’en récupérer le .swc sur le site du projet SwiftSuspenders.</p>
<h3>L’implémentation Robotlegs du MVC</h3>
<p>On l’a vu plus haut, le MVC est un pattern composé. On connaît maintenant la structure générale de Robotlegs et sa manière particulière de conserver un couplage lâche via l’injection de dépendances. Examinons maintenant les différents patterns constituants son implémentation de MVC :</p>
<ul>
<li><strong>la couche Model :</strong> elle est implémentée via un pattern Actor. Pattern qui n’en est pas vraiment un, en réalité, c’est simplement une classe capable d’émettre un événement.</li>
<li><strong>la couche Controller :</strong> elle est implémentée via un pattern Command. Chaque événement de l’application est mappé sur une Command. La Command est déclenchée quand l’événement survient. Au sein de la Command sont injectés les classes Model et View à modifier.</li>
<li><strong>la couche View : </strong>elle est implémentée via un pattern Mediator. Pour chaque composant visuel important, une classe Mediator est créée. Cette classe écoute le framework et agit sur la View, et écoute la View et émet vers le framework.</li>
</ul>
<p>Actor et Mediator émettent des événements, la couche Controller en écoute. Par souci de clarté, il est important voire capital de bien assimiler que les événements du framework ne doivent pas cohabiter avec les événements issus de la View : le moindre <em>MouseEvent.ROLL_OVER</em> n’a pas forcément vocation à remonter jusqu’au coeur du framework!</p>
<p>Tout ça est très compliqué. Heureusement il reste l&#8217;exemple pratique en 4 parties.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.entremidietdeux.com/decouverte-du-framework-robotlegs-15/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#171;&#160;Support your local scene&#160;&#187;</title>
		<link>http://www.entremidietdeux.com/support-your-local-scene/</link>
		<comments>http://www.entremidietdeux.com/support-your-local-scene/#comments</comments>
		<pubDate>Sun, 07 Nov 2010 19:26:04 +0000</pubDate>
		<dc:creator>fh</dc:creator>
				<category><![CDATA[pub-copinage]]></category>

		<guid isPermaLink="false">http://www.entremidietdeux.com/?p=68</guid>
		<description><![CDATA[Souvent on me demande de produire des sites Web, parfois j&#8217;accepte. Sorry Baby Studio est un studio d&#8217;enregistrement, local de répétition et prestataire en événementiel localisé à Lille. Une production Entre Midi et Deux &#8211; tous droits réservés Sorry Baby &#8230; <a href="http://www.entremidietdeux.com/support-your-local-scene/">Continuer la lecture <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Souvent on me demande de produire des sites Web, parfois j&#8217;accepte. <a href="http://www.sorrybabystudio.fr">Sorry Baby Studio</a> est un studio d&#8217;enregistrement, local de répétition et prestataire en événementiel localisé à Lille.</p>
<p><a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/sorrybaby-00.png"><img class="aligncenter size-full wp-image-69" title="sorrybaby-00" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/sorrybaby-00.png" alt="" width="450" height="284" /></a><span id="more-68"></span><a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/sorrybaby-01.png"><img class="aligncenter size-full wp-image-70" title="sorrybaby-01" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/sorrybaby-01.png" alt="" width="450" height="284" /></a><a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/sorrybaby-02.png"><img class="aligncenter size-full wp-image-71" title="sorrybaby-02" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/sorrybaby-02.png" alt="" width="450" height="284" /></a>Une production Entre Midi et Deux &#8211; tous droits réservés Sorry Baby Studio.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.entremidietdeux.com/support-your-local-scene/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Interagir avec un micro-contrôleur via USB en AS3</title>
		<link>http://www.entremidietdeux.com/interagir-avec-un-micro-controleur-via-usb-en-as3/</link>
		<comments>http://www.entremidietdeux.com/interagir-avec-un-micro-controleur-via-usb-en-as3/#comments</comments>
		<pubDate>Sun, 07 Nov 2010 17:21:51 +0000</pubDate>
		<dc:creator>fh</dc:creator>
				<category><![CDATA[Actionscript 3]]></category>
		<category><![CDATA[Arduino]]></category>
		<category><![CDATA[Flash Platform]]></category>
		<category><![CDATA[Flex]]></category>

		<guid isPermaLink="false">http://www.entremidietdeux.com/?p=40</guid>
		<description><![CDATA[La Flash Platform est un outil formidable lorsqu&#8217;il s&#8217;agit d&#8217;animation interactive sur le Web, et de développer des applications desktop via AIR. La plate-forme Flash offre d&#8217;autres possibilités peu connues. La simplicité du langage et la richesse de sa couche &#8230; <a href="http://www.entremidietdeux.com/interagir-avec-un-micro-controleur-via-usb-en-as3/">Continuer la lecture <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><iframe src="http://player.vimeo.com/video/14032556" width="400" height="308" frameborder="0"></iframe></p>
<p>La Flash Platform est un outil formidable lorsqu&#8217;il s&#8217;agit d&#8217;animation interactive sur le Web, et de développer des applications desktop via AIR. La plate-forme Flash offre d&#8217;autres possibilités peu connues. La simplicité du langage et la richesse de sa couche graphique en fait un outil idéal pour développer rapidement des applications visuelles, ce qui a commencé il y a quelques années à intéresser des artistes contemporains : installations interactives, objets intelligents, pilotage de robots, etc. La simplicité du langage AS3 a rendu accessible le développement d&#8217;oeuvres réagissant à leur environnement, ou agissant sur leur environnement : c&#8217;est le <a href="http://en.wikipedia.org/wiki/Physical_computing" target="_self">physical computing</a>.</p>
<p><span id="more-40"></span></p>
<p>Pour interagir avec l&#8217;environnement, nous avons besoin d&#8217;une interface physique, comportant des entrées (pour percevoir), et de sorties (pour agir). Notre interface sera :</p>
<ul>
<li>une puce électronique, le PIC en lui-même, alimenté en 5v,</li>
<li>dotée simplement d&#8217;<strong>entrées</strong> et de <strong>sorties</strong>,</li>
<li>ces entrées et ces sorties peuvent être <strong>analogiques</strong> ou <strong>numériques</strong> : soit elles renvoient/reçoivent une <strong>valeur</strong> (généralement de 0 à 255), soit un <strong>trigger</strong> (0 ou 1, noté HIGH ou LOW),</li>
<li> sur la puce, un espace de mémoire flash contient un programme. Ce programme détermine les interactions entre entrées/sorties, envoie certaines données vers le port série de la carte, les traite parfois, etc. L&#8217;espace est très restreint, mais permet pas mal de choses. Le programme est développé et compilé sur PC, puis flashé sur la puce, via un port série ou USB</li>
<li>un port USB, pour communiquer avec le PC, pour envoyer les données et en recevoir, pour flasher le code sur la puce, et aussi pour l&#8217;alimenter en énergie.</li>
</ul>
<p>Il existe des milliers de modèles différents de PIC. Certains se programment en C, en Assembleur, en Basic, ils sont vendus nus, ou parfois intégré à une carte facilitant leur usage. Des développeurs open-source ont élaboré une plate-forme matérielle et logicielle complète pour faciliter leur usage à destination des mortels : c&#8217;est la plate-forme <a href="http://arduino.cc/" target="_self">Arduino</a>.</p>
<p>Une Arduino est une carte doté d&#8217;un connecteur USB, d&#8217;un PIC ATMega168, de fiches d&#8217;entrées/sorties, et d&#8217;un quartz permettant de faire tourner tout ça. Vous pouvez soit récupérer les plans et construire votre carte vous-mêmes, soit acheter directement une carte prête à l&#8217;emploi : l&#8217;Arduino Duemilanove, pour la somme modique de 22€, <a href="http://www.smartprj.com/catalog/index.php?main_page=product_info&amp;cPath=1&amp;products_id=1" target="_self">auprès de l&#8217;initiateur du projet</a>.</p>
<h2>Côté hardware :</h2>
<p><span style="font-family: Georgia, 'Bitstream Charter', serif; line-height: 24px; font-size: 16px;">Une carte Arduino ça ressemble à ça :</span></p>
<p style="text-align: center;"><img class="size-full wp-image-44 aligncenter" title="Une carte Arduino" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/arduinoq.png" alt="" width="320" height="268" /></p>
<p>Vous aurez aussi besoin de quelques composants :</p>
<ul>
<li>une résistance photo-sensible et une résistance de 10KOhms (les couleurs sur la résistance servent à indiquer sa valeur). Vous pouvez vous procurez ça dans tous les magasins d&#8217;électronique, à 0,10€ pièce.<a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/resistances.png"><img class="aligncenter size-full wp-image-45" title="resistances" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/resistances.png" alt="" width="400" height="274" /></a></li>
<li>une platine d&#8217;expérimentation et les fils qui vont bien. A acheter sur eBay aux usines chinoises pour 9€, ou en magasin pour 30€.<a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/breadboard.png"><img class="aligncenter size-full wp-image-46" title="breadboard" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/breadboard.png" alt="" width="221" height="193" /></a></li>
<li>de la lumière <img src='http://www.entremidietdeux.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
</ul>
<h2>Côté software :</h2>
<ul>
<li><a href="http://arduino.cc/en/Main/Software" target="_blank">les drivers Arduino et l&#8217;IDE de développement</a></li>
<li>Flash/Flex SDK selon vos habitudes,</li>
<li>la librairie <a href="http://code.google.com/p/as3glue/" target="_blank">AS3Glue</a>, qui permet à Flash de communiquer avec l&#8217;Arduino,</li>
<li>j&#8217;ai utilisé <a href="http://arthropod.stopp.se/" target="_blank">Arthropod</a> comme debugger,</li>
<li>j&#8217;ai utilisé <a href="http://away3d.com/downloads" target="_blank">Away3D</a> comme moteur 3D,</li>
</ul>
<h2>Installation</h2>
<p>Commençons par installer l&#8217;environnement logiciel nécessaire à l&#8217;Arduino.</p>
<ul>
<li>la carte n&#8217;est pas branchée au PC.</li>
<li>on télécharge le <a href="http://arduino.cc/en/Main/Software" target="_blank">.zip</a>, on le dézippe proprement, dans <em>C:\tools\arduino</em> par exemple.</li>
<li>on connecte la carte sur un port USB : Windows démarre la recherche de nouveau matériel une première fois, on clique sur <strong>&#8230;(utilisateurs expérimentés)<a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installstep1.png"><img class="aligncenter size-full wp-image-48" title="installstep1" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installstep1.png" alt="" width="503" height="420" /></a><br />
</strong></li>
<li>on sélectionne <em>C:\tools\arduino\drivers\FTDI USB drivers</em> dans la fenêtre suivante<a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installstep2.png"><img class="aligncenter size-full wp-image-49" title="installstep2" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installstep2.png" alt="" width="503" height="420" /></a></li>
<li>une fois cliqué sur <strong>Terminé</strong>, re-belote, 2ème recherche de drivers, on refait la même chose.</li>
<li>Les 2 drivers que nous avons installés servent à faire apparaître le port USB de la carte comme un port série. En effet, nous pouvons maintenant vérifier notre installation : <a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installstep3.png"><img class="aligncenter size-full wp-image-50" title="installstep3" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installstep3.png" alt="" width="520" height="538" /></a></li>
<li>Le port COM3 correspond maintenant à notre carte. Toute donnée entrante/sortante sur ce port série sera en réalité vers/depuis le PIC par le port USB physique. Le port USB émule un port série.</li>
<li>Lançons maintenant l&#8217;IDE, C:\tools\arduino\arduino.exe<a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installstep4.png"><img class="aligncenter size-full wp-image-51" title="installstep4" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installstep4.png" alt="" width="500" height="600" /></a></li>
<li>Lors de votre première utilisation, vous devez au préalable configurer l&#8217;IDE pour le modèle de votre carte :<a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installstep5.png"><img class="aligncenter size-full wp-image-52" title="installstep5" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installstep5.png" alt="" width="648" height="360" /></a></li>
<li>Et configurer le bon port série :<a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installstep6.png"><img class="aligncenter size-full wp-image-53" title="installstep6" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installstep6.png" alt="" width="501" height="601" /></a></li>
<li>We&#8217;re done!</li>
</ul>
<h2>Un premier test</h2>
<p>La plate-forme Arduino n&#8217;utilise pas le C ou l&#8217;Assembleur pour programmer le PIC, mais un langage très simple, proche de Processing, nommé <a href="http://wiring.org.co/reference/" target="_blank">Wiring</a>. Nous allons développer notre première appli Wiring pour tester notre install, la flasher sur la carte et tester si tout se passe correctement.</p>
<p>La carte comporte une toute petite LED, nous n&#8217;avons pas besoin d&#8217;en connecter une. Cette LED est pilotée par le connecteur n°13, en envoyant HIGH/LOW à intervalles réguliers, elle devrait clignoter. Voici le code-source :</p>
<p>[c]<br />
#define LED 13</p>
<p>void setup()<br />
{<br />
    pinMode( LED, OUTPUT );<br />
}</p>
<p>void loop()<br />
{<br />
    digitalWrite( LED, HIGH );<br />
    delay(1000);<br />
    digitalWrite( LED, LOW );<br />
    delay(1000);<br />
}[/c]</p>
<p>On compile le code en cliquant sur le bouton de l&#8217;IDE. Tout se passe bien. Maintenant nous uploadons l&#8217;éxecutable sur la carte en cliquant sur &laquo;&nbsp;Upload&nbsp;&raquo;. Les diodes de la carte RX et TX clignotent, confirmant le flashage, puis la diode L commence à clignoter tous les 1000ms.</p>
<h2>Création du circuit électronique du capteur</h2>
<h3>a) Partie hardware</h3>
<p>Nous allons maintenant construire notre capteur pour l&#8217;application définitive. Le but du montage est le suivant :</p>
<ul>
<li>la photo-résistance a une résistance variable selon la lumière.</li>
<li>le micro-contrôleur va lire cette valeur en continu</li>
<li>et la transmettre au PC via l&#8217;USB, sous forme d&#8217;une valeur numérique exploitable par notre client Flash.</li>
</ul>
<p><strong>ATTENTION : nous allons maintenant réaliser le montage électronique nécessaire. nous allons donc jouer avec l&#8217;électricité.</strong> L&#8217;Arduino fonctionne en 5V, il n&#8217;y a aucun danger pour vous. Prenez vos précautions en réalisant le montage, ne branchez pas le GND sans avoir vérifié plusieurs fois qu&#8217;il est bien fidèle à la photo. Même si l&#8217;Arduino possède un fusible, une erreur mineure peut provoquer un court-circuit et détruire un de vos composants, soyez attentifs à ce que vous faites &#8211; faites particulièrement attention aux fils qui trainent en cours de branchement et peuvent se toucher. Si vous provoquez un court-circuit, débranchez le GND, puis débranchez et rebranchez la carte du connecteur USB, le fusible se remettra en place.</p>
<p>Réalisez le montage électronique suivant sur votre platine :</p>
<p><a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/circuitk.png"><img class="aligncenter size-full wp-image-54" title="circuitk" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/circuitk.png" alt="" width="527" height="750" /></a></p>
<p>Le circuit est plutôt simple :</p>
<ul>
<li>le +5V de la carte est utilisé,</li>
<li>il passe au travers de la résistance variable, suivant la lumière qu&#8217;elle capte, sa résistance varie,</li>
<li>elle est protégée par une résistance 10KOhms,</li>
<li>l&#8217;entrée analogique 0 lit la valeur de tension dans le circuit,</li>
<li>le circuit se finit sur le pôle négatif (GND = &laquo;&nbsp;ground&nbsp;&raquo;) de la carte.</li>
</ul>
<h3>b) Partie software</h3>
<p>Voilà le code source :</p>
<p>[c]
<pre><code>
#define SENSOR 0 // la photo-résistance sera branchée sur ce port

int lightValue = 0;

void setup()
{
    // on démarre le port série, à 9600 Bauds
    Serial.begin(9600);
}

void loop()
{
    // on lit la valeur analogique sur le pin 0
    lightValue = analogRead(SENSOR);</code>

    // on l'envoie dans le port série
    Serial.println(lightValue);</code>

    // on ralentit 100ms, sinon le port va saturer
    // la carte tourne en effet à la vitesse de son quartz,
    // soit 20MHz
    delay(100);
}
</code></pre>
<p>[/c]</p>
<p>On compile, on uploade.</p>
<p>Dans l&#8217;IDE, vous pouvez contrôler les infos envoyées par la carte sur la port série par la fenêtre de monitoring, lancez-la :</p>
<p><a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/testlightsensor.png"><img class="aligncenter size-full wp-image-55" title="testlightsensor" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/testlightsensor.png" alt="" width="526" height="528" /></a></p>
<p>La valeur oscille entre 0 et 1023 en théorie, en pratique les photos-résistances sont tellement sensibles que ça dépend de la lumière ambiante.</p>
<p>Oh yeah, ça marche, il ne reste plus qu&#8217;à rendre ça lisible par Flash!</p>
<h3>c) Développement du client Flash</h3>
<p>Voici le schéma de ce que nous allons réaliser :</p>
<p><a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/schemaarduino.png"><img class="aligncenter size-full wp-image-56" title="schemaarduino" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/schemaarduino.png" alt="" width="550" height="550" /></a></p>
<p>Nous avons précédemment écrit en Wiring notre partie logicielle, puis l&#8217;avons flashée sur le PIC. Ce n&#8217;est pas très flexible : on doit flasher à chaque fois, apprendre un nouveau langage, et nous sommes limités à la toute petite mémoire de 8Ko du PIC, c&#8217;est pas génial.</p>
<p>Dans ce but, la communauté open-source a développé Firmata. C&#8217;est un firmware standardisé, qui va nous permettre de contrôler le PIC depuis l&#8217;extérieur. Au lieu de flasher des instructions, on va envoyer ces instructions au firmware par le port série, qui les interprétera en temps réel.</p>
<p>Installer Firmata est très simple, il est fourni dans les exemples de l&#8217;IDE Arduino :</p>
<p><a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installfirmata.png"><img class="aligncenter size-full wp-image-57" title="installfirmata" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/installfirmata.png" alt="" width="459" height="483" /></a></p>
<p>On clique sur &laquo;&nbsp;Upload&nbsp;&raquo;, notre précédent programme est effacé, remplacé par le firmware.</p>
<p>Flash ne peut pas lire directement les données du port série, ou du port USB. Mais il peut communiquer via un serveur socket. Nous allons donc utiliser un petit utilitaire qui va mapper les données du port série vers un socket, il s&#8217;agit de <strong>serproxy</strong>. Celui-ci est directement inclus dans le package <strong>AS3Glue</strong>, dans le dossier <strong>\applications\Serproxy-0.1.3-3</strong>.</p>
<p>Il y a un peu de configuration à faire &#8211; éditez le fichier config.cfg pour rfléter votre configuration,  avec l&#8217;Arduino sur le port COM3, voilà la configuration à suivre :</p>
<pre><code>newlines_to_nils=false
comm_ports=3
comm_baud=57600
comm_databits=8
comm_stopbits=1
comm_parity=none
timeout=300
net_port3=5331
</code></pre>
<p>Pour vérifier la configuration de serproxy, utilisez sous Windows la commande telnet (<em>Démarrer &gt; Exécuter &gt; telnet</em>), après avoir lancé serproxy.exe :</p>
<pre><code>Microsoft Telnet&gt; o 127.0.0.1 5331</code></pre>
<p>Le serveur doit afficher :</p>
<pre><code>server(3) : thread started</code></pre>
<p>En cas d&#8217;erreur de ce type :</p>
<pre><code>unable to open comm port</code></pre>
<p>vérifiez votre port COM dans le fichier serproxy.cfg, et fermez le SerialMonitor : une seule application à la fois peut utiliser un port COM sous Windows.</p>
<p>Notre PIC est maintenant muni du firmware Firmata 2.0. Il reste à le piloter, <em>as3glue</em> est un projet open-source qui implémente la norme Firmata aussi. Rien de plus simple donc :</p>
<p>[as3]<br />
package<br />
{<br />
	import com.carlcalderon.arthropod.Debug;</p>
<p>	import flash.display.Sprite;<br />
	import flash.events.Event;</p>
<p>	import net.eriksjodin.arduino.Arduino;<br />
	import net.eriksjodin.arduino.events.ArduinoEvent;<br />
	/**<br />
	* &#8230;<br />
	* @author<br />
	*/<br />
	public class Application extends Sprite<br />
	{<br />
		private var _arduino : Arduino;</p>
<p>		public function Application() : void<br />
		{<br />
			Debug.clear();<br />
			_arduino = new Arduino( &#8217;127.0.0.1&#8242;, 5331 );<br />
			_arduino.addEventListener( Event.CONNECT, onSocketConnect );<br />
			_arduino.addEventListener( ArduinoEvent.FIRMWARE_VERSION, onReceiveFirmwareVersion );<br />
			_arduino.addEventListener( ArduinoEvent.ANALOG_DATA, onReceiveAnalogData );<br />
		}</p>
<p>		private function onSocketConnect( e : Event ) : void<br />
		{<br />
			Debug.log(&#8216;onSocketConnect&#8217;);<br />
			_arduino.requestFirmwareVersion();<br />
		}</p>
<p>		private function onReceiveFirmwareVersion( e : ArduinoEvent ) : void<br />
		{<br />
			// on contrôle ainsi si Firmata 2.0 est bien présent<br />
			Debug.log( &#8216;firmware version : &#8216; + e.value );</p>
<p>			// strictement équivalent à notre programme Wiring :<br />
			// Serial.println(analogRead(0));<br />
			_arduino.setAnalogPinReporting( 0, Arduino.ON );<br />
		}</p>
<p>		private function onReceiveAnalogData( e : ArduinoEvent ) : void<br />
		{<br />
			// on affiche la valeur,<br />
			// qui doit être équivalente à celle affichée par notre programme Wiring<br />
			Debug.log( e.value );<br />
		}<br />
	}<br />
}<br />
[/as3]</p>
<p>Si vous avez bien suivi les étapes, votre débuggeur vous affiche la valeur de lumière ambiante, voilà les erreurs possibles :</p>
<ul>
<li><em>unable to open comm port </em>affiché dans la fenêtre de serproxy : fermez toutes vos applications, recommencez &#8211; une de vos applications monopolise le port COM3.</li>
<li>Le débuggueur reste bloqué à <em>onSocketConnect</em> : Firmata 2.0 n&#8217;est pas correctement flashé sur la carte. On la débranche de l&#8217;USB, on rebranche, on reflashe depuis l&#8217;IDE Arduino. Si ça ne suffit pas, vérifiez la vitesse en bauds définie dans serproxy.cfg, Firmata nécessite 57600.</li>
<li>ma carte a l&#8217;air morte : le fusible est déclenché, votre circuit est mal monté. On refait le circuit, on débranche, on rebranche.</li>
]]></content:encoded>
			<wfw:commentRss>http://www.entremidietdeux.com/interagir-avec-un-micro-controleur-via-usb-en-as3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Remplacer l’EventDispatcher par les Signals</title>
		<link>http://www.entremidietdeux.com/remplacer-l%e2%80%99eventdispatcher-par-les-signals/</link>
		<comments>http://www.entremidietdeux.com/remplacer-l%e2%80%99eventdispatcher-par-les-signals/#comments</comments>
		<pubDate>Sun, 07 Nov 2010 16:15:50 +0000</pubDate>
		<dc:creator>fh</dc:creator>
				<category><![CDATA[Actionscript 3]]></category>
		<category><![CDATA[Flash Platform]]></category>
		<category><![CDATA[Flex]]></category>

		<guid isPermaLink="false">http://www.entremidietdeux.com/?p=29</guid>
		<description><![CDATA[Cet article vous présente la librairie Signals de Robert Penner, qui a vocation à remplacer la couche événementielle de Flash. 1) l&#8217;EventDispatcher d&#8217;Adobe Actuellement, les applications AS3 se structurent en objets, qui communiquent par des événements (aka. “events”). On utilise &#8230; <a href="http://www.entremidietdeux.com/remplacer-l%e2%80%99eventdispatcher-par-les-signals/">Continuer la lecture <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Cet article vous présente la librairie Signals de Robert Penner, qui a vocation à remplacer la couche événementielle de Flash.</p>
<h2>1) l&#8217;EventDispatcher d&#8217;Adobe</h2>
<p>Actuellement, les applications AS3 se structurent en objets, qui communiquent par des événements (aka. “events”).  On utilise les objets par défaut de l’API Flash (MovieClip, Sprite, etc), on en ajoute parfois selon le projet, et on fait communiquer tout ça par des events personnalisés toujours selon le projet. Développons donc tout ceci dans un petit rappel.</p>
<p><span id="more-29"></span></p>
<h3>a) Le cas des composants visuels</h3>
<p>Soit un type Foo, héritant de MovieClip, défini par la classe Foo suivante :</p>
<p>[as3]<br />
package components<br />
{<br />
import events.FooEvent;</p>
<p>import flash.display.MovieClip;<br />
import flash.events.MouseEvent;</p>
<p>public class Foo extends MovieClip<br />
{<br />
public function Foo()<br />
{<br />
graphics.beginFill(0&#215;000000, 1);<br />
graphics.drawCircle(0, 0, 5);<br />
graphics.endFill();</p>
<p>addEventListener( MouseEvent.CLICK, onClick );<br />
}</p>
<p>private function onClick( e : MouseEvent ) : void<br />
{<br />
dispatchEvent( new FooEvent(FooEvent.FOO) );<br />
}<br />
}<br />
}<br />
[/as3]</p>
<p>J’ai ici un composant visuel héritant de MovieClip, dispatchant un événement FooEvent au clic (ce qui est rigoureusement inutile). Si ce composant est capable de dispatcher via la méthode dispatchEvent, c’est que cette méthode est le résultat de la longue chaîne d’héritage suivante :</p>
<p><strong>Foo &lt; MovieClip &lt; Sprite &lt; DisplayObjectContainer &lt; InteractiveObject &lt; DisplayObject &lt; EventDispatcher</strong></p>
<p>On a défini à côté, dans une classe FooEvent clairement séparée, notre événement personnalisé :</p>
<p>[as3]<br />
package events<br />
{<br />
import flash.events.Event;</p>
<p>public class FooEvent extends Event<br />
{<br />
public static const FOO : String = &#8216;foo&#8217;;</p>
<p>public function FooEvent(	type : String,<br />
bubbles : Boolean = false,<br />
cancelable : Boolean = false )<br />
{<br />
super(type, bubbles, cancelable);<br />
}<br />
}<br />
}<br />
[/as3]<br />
On en conclut donc que les composants visuels sont capables de dispatcher des événements grâce au fait qu’ils héritent indirectement de la classe EventDispatcher.</p>
<h3>b) Le cas des composants non-visuels</h3>
<p>Soit cette fois-ci un type Bar, défini par la classe suivante :</p>
<p>[as3]<br />
package components<br />
{<br />
import events.FooEvent;</p>
<p>import flash.events.EventDispatcher;</p>
<p>public class Bar extends EventDispatcher<br />
{<br />
public function Bar()<br />
{</p>
<p>}</p>
<p>public function emitFooEvent() : void<br />
{<br />
dispatchEvent( new FooEvent(FooEvent.FOO) );<br />
}<br />
}<br />
}<br />
[/as3]</p>
<p>On voit que pour pouvoir émettre un événement, on est obligé d’hériter directement cette fois-ci de l’EventDispatcher. En effet, notre objet n’héritant de personne, on ne peut profiter d’un héritage indirect, donc on amène explicitement cette héritage.</p>
<h3>c) Le cas des écouteurs</h3>
<p>Les objets écoutant Foo ou Bar, utilisent une syntaxe de ce type :</p>
<p>[as3]<br />
// notez l’import de FooEvent<br />
import components.Bar;<br />
import events.FooEvent</p>
<p>var bar : Bar = new Bar();<br />
bar.addEventListener( FooEvent.FOO, onFoo );</p>
<p>// notez l’argument typé FooEvent du handler onFoo<br />
function onFoo( e : FooEvent ) : void<br />
{<br />
// &#8230;<br />
}<br />
[/as3]</p>
<h2>2) Critique du modèle actuel</h2>
<p>Le modèle actuel est plutôt propre à la lecture, respecte les standards actuels et reste relativement simple. Mais on peut faire mieux, puisque avec un peu de recul, des failles assez gênantes apparaissent :</p>
<ul>
<li><strong>“composition vs. héritage” :</strong> on voit que les fonctionnalités événementielles sont systèmatiquement héritées, or l’héritage est à éviter en P.O.O.</li>
<li><strong>les événements n&#8217;apparaissent pas dans l&#8217;interface de la classe :</strong> en P.O.O., une classe est toujours définie par une interface. On code <em>&laquo;&nbsp;contre une interface et non contre une implémentation&nbsp;&raquo;</em>. Hors avec le système d&#8217;EventDispatcher, à aucun moment les Events dispatchés par une classe n&#8217;apparaissent en clair &#8211; on ne peut avoir que des <em>dispatchEvent()</em> noyés dans le code d&#8217;implémentation.</li>
<li><strong>“boiler-plate code” :</strong> la syntaxe est longue, et pourtant une des plus courantes à taper sur un projet, même de petite taille</li>
<li><strong>nécessite la définition d’une classe FooEvent :</strong> on est obligé de définir, allouer de la mémoire, et instancier cette classe héritant d’Event pour pouvoir bosser proprement.</li>
</ul>
<h2>3) AS3 Signals, une alternative réussie</h2>
<p>Sur ces bases, Robert Penner a développé une alternative open-source à l’EventDispatcher, la librairie AS3 Signals. Il avait deux objectifs : la performance, et la beauté du code. Il a donc fait l’inverse d’Adobe :</p>
<ul>
<li><strong>utilisable par composition,</strong></li>
<li><strong>syntaxe claire et concise,</strong></li>
<li><strong>pas de classe Event à définir.</strong></li>
</ul>
<p>AS3 Signals se télécharge<a title="AS3 Signals" href="http://github.com/robertpenner/as3-signals" target="_self"> ici</a>, sous forme de .SWC en cliquant sur “downloads”, ou sous forme de sources via Git.</p>
<h3>a) syntaxe générale</h3>
<p>Adaptons notre classe Bar pour qu’elle utilise, non pas l’EventDispatcher, mais un Signal, et analysons la syntaxe :</p>
<p>[as3]<br />
package components<br />
{<br />
import org.osflash.signals.Signal;</p>
<p>public class Bar<br />
{<br />
public var foo : Signal;</p>
<p>public function Bar()<br />
{<br />
foo = new Signal();<br />
}</p>
<p>public function emitFooSignal() : void<br />
{<br />
foo.dispatch();<br />
}<br />
}<br />
}<br />
[/as3]</p>
<p>On constate que la classe Bar n’hérite plus de personne, et ça c’est bien. On constate aussi le dispatch est réduit à sa plus simple expression, et ça c’est encore mieux.</p>
<p>Jetons un oeil du côté d’un objet écoutant une instance de Bar via un Signal :</p>
<p>[as3]<br />
import components.Bar;</p>
<p>var bar : Bar = new Bar();<br />
bar.foo.add( onFoo );</p>
<p>function onFoo() : void<br />
{<br />
// &#8230;<br />
}<br />
[/as3]</p>
<p>Pour le retrait des écouteurs, la syntaxe est toute aussi simple :</p>
<p>[as3]<br />
bar.foo.remove( onFoo );<br />
[/as3]</p>
<p>On peut même, et c&#8217;est un atout majeur, retirer l&#8217;ensemble des écouteurs en une instruction :</p>
<p>[as3]<br />
bar.foo.removeAll();<br />
[/as3]</p>
<p>Enfin, un peu de &laquo;&nbsp;syntactic sugar&nbsp;&raquo;, pour les écouteurs à usage unique, le retrait peut être automatisé par :</p>
<p>[as3]<br />
bar.foo.addOnce(onFoo);<br />
[/as3]</p>
<h3>b) passage de valeurs via un Signal</h3>
<p>Nous allons maintenant passer des données entre Bar et son écouteur via un Signal. Avec le modèle classique de l’EventDispatcher, ça nous obligerait à éditer FooEvent, pour lui rajouter une propriété fooEvent.baz. Essayons d’arriver à un résultat similaire via un Signal :</p>
<p>[as3]<br />
package components<br />
{<br />
import org.osflash.signals.Signal;</p>
<p>public class Bar<br />
{<br />
public var foo : Signal;</p>
<p>public function Bar()<br />
{<br />
foo = new Signal();<br />
}</p>
<p>public function emitFooSignal() : void<br />
{<br />
foo.dispatch(‘baz’, ‘quux’);<br />
}<br />
}<br />
}<br />
[/as3]</p>
<p>La méthode Signal.dispatch accepte un nombre N d’arguments, quel que soit leur type. Il suffit de passer directement la/les valeurs, pour qu’elles soient accessibles côté écouteur, dont nous modifions le code en ce sens :</p>
<p>[as3]<br />
import components.Bar;</p>
<p>var bar : Bar = new Bar();<br />
bar.foo.add( onFoo );</p>
<p>function onFoo( baz : String, quux : String ) : void<br />
{<br />
// &#8230;<br />
}<br />
[/as3]</p>
<p>On notera que le type des arguments <em>baz</em> et <em>quux</em> est préservé, assurant la sécurité habituelle à la compilation.</p>
<p>L&#8217;EventDispatcher passe lui systématiquement une instance &laquo;&nbsp;complète&nbsp;&raquo; d&#8217;Event, avec notamment la propriété <em>Event.target</em>, particulièrement utilisée sous sa forme <em>MouseEvent.currentTarget</em> pour de la détection d&#8217;interaction utilisateur. Ce passage d&#8217;un Event est<strong> implicite</strong> et systématique. Sous Signals, il est <strong>explicite</strong>, c&#8217;est au développeur de passer ce dont il a besoin &#8211; car chaque <em>Event.target</em> inutilisé, c&#8217;est un peu de mémoire occupée&#8230; Par exemple :</p>
<p>[as3]<br />
// au sein de mon composant visuel FooComponent<br />
backToHomeClick.dispatch( this );<br />
[/as3]</p>
<p>[as3]<br />
// côté écouteur, on reçoit bien l&#8217;émetteur, typé correctement,<br />
private function onClickDetected( target : FooComponent ) : void<br />
{<br />
trace(target.name);<br />
}<br />
[/as3]</p>
<p>On notera qu&#8217;il n&#8217;est pas nécessaire de caster <em>target</em> vers son type original, contrairement à l&#8217;EventDispatcher (<em>var _target : FooComponent = e.target as FooComponent</em>).</p>
<h3>c) cas d&#8217;événements multiples</h3>
<p>Dans le cas d&#8217;une classe diffusant plusieurs événements de types différents, on créera plusieurs Signals distincts, prenons l&#8217;exemple d&#8217;une classe plus complexe, par exemple une couche d&#8217;abstraction pour l&#8217;API Facebook AS3 :</p>
<p>[as3]<br />
package components<br />
{<br />
import org.osflash.signals.Signal;</p>
<p>public class FacebookService<br />
{<br />
public var authenticationComplete : Signal;<br />
public var authenticationFail : Signal;<br />
public var friendsListComplete : Signal;<br />
public var friendsListPhotosComplete : Signal;</p>
<p>public function FacebookService()<br />
{<br />
// &#8230;;<br />
}</p>
<p>public function authenticate(&#8230;) : void<br />
{<br />
// &#8230;;<br />
}</p>
<p>// &#8230;<br />
}<br />
}<br />
[/as3]</p>
<p>Les Signals sont ensuite écoutés séparément, de la même manière que décrit ci-dessus, via la méthode publique Signal.add();</p>
<p>[as3]<br />
fbService.authenticationComplete.add( onAuth );<br />
fbService.authenticationFail.add( onAuthFail );<br />
fbService.listFriendsComplete.add( onFriends );<br />
fbService.listFriendsPhotos.add( onFriendsPhotos );<br />
[/as3]</p>
<h3>4) Pour finir</h3>
<p>Au-delà du gain en terme de syntaxe, la suppression de l’héritage, de la classe Event personnalisé induit évidemment un gain en terme de performance. Et ce gain est énorme : <a href="http://alecmce.com/as3/events-and-signals-performance-tests" target="_self">http://alecmce.com/as3/events-and-signals-performance-tests</a></p>
<p>Vu le gain de perfs, et de confort pour le développeur, Signals s’est imposé outre-Atlantique en quelques mois. On trouve maintenant par exemple un add-on <a title="Signals pour Robotlegs" href="http://joelhooks.com/2010/01/16/robotlegs-image-gallery-example-using-as3-signals-and-the-presentation-model/" target="_self">Signals pour Robotlegs</a>, transformant ce micro-framework déjà véloce en bête de course.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.entremidietdeux.com/remplacer-l%e2%80%99eventdispatcher-par-les-signals/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Créer une application multi-utilisateurs avec AS3 et node.js</title>
		<link>http://www.entremidietdeux.com/creer-une-application-multi-utilisateurs-avec-as3-et-node-js/</link>
		<comments>http://www.entremidietdeux.com/creer-une-application-multi-utilisateurs-avec-as3-et-node-js/#comments</comments>
		<pubDate>Sun, 07 Nov 2010 12:45:11 +0000</pubDate>
		<dc:creator>fh</dc:creator>
				<category><![CDATA[Actionscript 3]]></category>
		<category><![CDATA[Flash Platform]]></category>
		<category><![CDATA[Flex]]></category>

		<guid isPermaLink="false">http://www.entremidietdeux.com/?p=10</guid>
		<description><![CDATA[Amusons-nous un peu avec node.js, le plus trendy des projets open-source. Traditionnellement, les serveurs utilisés par les applications multi-utilisateurs temps-réel sont soit des solutions commerciales ou open-source (SmartFox, ElectroServer, Flash Media Server, Red5, etc), soit des développements propriétaires, réalisés dans des langages &#8230; <a href="http://www.entremidietdeux.com/creer-une-application-multi-utilisateurs-avec-as3-et-node-js/">Continuer la lecture <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.entremidietdeux.com/wp-content/uploads/2010/11/nodejs-logo.png"><img class="alignright size-full wp-image-17" title="Node.js" src="http://www.entremidietdeux.com/wp-content/uploads/2010/11/nodejs-logo.png" alt="Node.js" width="200" height="68" /></a>Amusons-nous un peu avec node.js, le plus trendy des projets open-source. Traditionnellement, les serveurs utilisés par les applications multi-utilisateurs temps-réel sont soit des solutions commerciales ou open-source (<a title="Smart Fox Server" href="http://www.smartfoxserver.com/products/" target="_self">SmartFox</a>, <a title="ElectroServer" href="http://www.electro-server.com/downloads.aspx" target="_self">ElectroServer</a>, <a title="Flash Media Server" href="http://www.adobe.com/fr/products/flashmediaserver/" target="_self">Flash Media Server</a>, <a title="Red5" href="http://red5.org/" target="_self">Red5</a>, etc), soit des développements propriétaires, réalisés dans des langages susceptibles de supporter des charges importantes (Java, C++, <a title="Stackless Python" href="http://www.stackless.com/" target="_self">Python Stackless</a> pour Eve Online, <a title="Erlang" href="http://www.erlang.org/" target="_self">Erlang</a> pour le chat Facebook). Les serveurs sont généralement multi-threadés et synchrones : un thread est alloué par client connecté, avec un segment de pile dédié, le tout reste en mémoire pendant la durée de connexion du client au serveur &#8211; à l&#8217;exception d&#8217;Erlang et de Stackless justement.</p>
<h2>Pourquoi node.js ?</h2>
<p>Node.js est un projet open-source basé sur l’interpréteur Javascript V8 de Google. C&#8217;est un interpréteur Javascript côté serveur, auquel a été ajoutée une API réseau de bas niveau dotée par exemple des fonctionnalités HTTP, sockets, UDP. C&#8217;est donc un toolkit destiné à concevoir des serveurs. Il utilise Javascript comme langage de script, un langage simple, proche de l’AS3, et donc rapide à assimiler pour le développeur Flash de base. Tout le monde connait quelques bouts de Javascript.</p>
<p>Node.js possède deux caractèristiques supplémentaires qui font tout son intérêt :</p>
<ul>
<li><strong>la programmation évènementielle :</strong> 100% basé sur les gestionnaires d’évènements, en node.js <em>“tout est évènement”</em>. Hors de la durée de vie d&#8217;un évènement, le serveur &laquo;&nbsp;dort&nbsp;&raquo;.</li>
<li><strong>son architecture interne :</strong> comme le serveur Web <a title="nginx" href="http://nginx.org/" target="_self">nginx</a>, il n’utilise pas une thread par client, donc il gère très bien <a href="http://en.wikipedia.org/wiki/C10k_problem" target="_self">les problèmes de concurrence</a>, ce qui en fait l&#8217;outil idéal pour les problématiques de serveurs multi-utilisateurs temps-réel. Chose à savoir, Nginx s&#8217;est hissé en un an au 3ème rang des serveurs Web les plus déployés. Le micro-threading, c&#8217;est l&#8217;avenir.</li>
</ul>
<p><span id="more-10"></span></p>
<p>Quelques liens complémentaires :</p>
<ul>
<li>Le site officiel comporte un Hello World assez simple : <a href="http://nodejs.org/" target="_self">Hello World</a>.</li>
<li>Un excellent article de vulgarisation en français : <a href="http://blog.inovia-conseil.fr/?p=101">le blog d&#8217;Inovia</a>.</li>
<li>Les sources d&#8217;un serveur de chat complet par l&#8217;auteur de node.js, qui m&#8217;ont servies de base de travail : <a href="http://github.com/ry/node_chat" target="_self">node_chat sur GitHub</a>.</li>
<li>La <a href="http://nodejs.org/api.html" target="_self">documentation complète</a> de l&#8217;API comporte d&#8217;autres exemples de serveurs.</li>
</ul>
<h2>Installation de node.js</h2>
<p>Les pré-requis pour pouvoir installer node.js sont les suivants :</p>
<ul>
<li>node.js ne tourne que sous Linux,</li>
<li><em>gcc</em> et <em>make</em> doivent être installés<br />
<span style="font-size: 13px; font-family: Monaco, Consolas, 'Andale Mono', 'DejaVu Sans Mono', monospace; line-height: 19px;">sudo apt-get install gcc</span></li>
<li>pas de proxy sur le(s) port(s) utilisé(s),</li>
<li>le(s) port(s) utilisés doivent être ouverts côté routeur/firewall et redirigés vers le serveur.</li>
<li>si vous ne disposez pas encore d&#8217;un serveur : un PC sous Ubuntu suffit. Il n&#8217;est PAS nécessaire d&#8217;installer Apache/MySQL/PHP, seul les packages <em>git-core</em>, <em>open-ssh</em>, <em>make</em> et <em>gcc</em> sont nécessaires.</li>
</ul>
<p>Commençons par récupérer les sources via Git :</p>
<p><code>$ mkdir ~/temp<br />
$ git clone git://github.com/ry/node.git<br />
$ cd node<br />
</code></p>
<p>Nous voilà munis des dernières sources. On compile ces sources via la manière traditionnelle :</p>
<p><code> $./configure<br />
</code></p>
<p>L’absence d’OpenSSL provoque un warning, mais n’est pas bloquante.</p>
<p><code>$ make<br />
</code></p>
<p>La compilation dure 5-10 minutes sur un Core Quad, puisque V8 est inclus dans les sources.</p>
<p><code> $ sudo make install<br />
</code></p>
<p>Nous pouvons maintenant vérifier que tout fonctionne correctement :</p>
<p><code>$ node -v<br />
v0.1.103<br />
</code></p>
<p>Node et ses dépendances sont installés dans <em>/usr/local/bin</em>.</p>
<h2>Prise en main de node.js : servir proprement le policy file</h2>
<p>Depuis Flash Player 9, toutes les applications requérant par la suite un accès vers un socket distant initie l’échange client-serveur en interrogeant le serveur sur le port 843, en attendant en retour un fichier de sécurité autorisant ou non l’accès.</p>
<p>Typiquement, ce genre de chose est intégré aux serveurs commerciaux, ou alors il est requis d’installer le <a href="http://www.adobe.com/devnet/flashplayer/articles/socket_policy_files.html" target="_self">serveur de policy file proposé par Adobe</a>. Commençons par développer notre propre serveur de Policy File, histoire de tâter un peu le terrain.</p>
<p>Voici la source Javascript du serveur :</p>
<p>[javascript]<br />
// importation des modules nécessaires<br />
var net = require(&#8216;net&#8217;),<br />
path = require(&#8216;path&#8217;),<br />
fs = require(&#8216;fs&#8217;);</p>
<p>// création du serveur<br />
net.createServer( function (socket) {</p>
<p>// réglage de l&#8217;encodage sur UTF-8<br />
socket.setEncoding(&laquo;&nbsp;utf8&#8243;);</p>
<p>// on forge le path complet de notre fichier .xml<br />
var securityFileName = path.join( process.cwd(), &#8216;policy.xml&#8217; );</p>
<p>// s’il existe<br />
path.exists( securityFileName, function(exists) {</p>
<p>fs.readFile( securityFileName, &laquo;&nbsp;binary&nbsp;&raquo;, function( err, file ) {</p>
<p>// on l’envoie au client par le socket sur le port 843<br />
socket.write( file, &laquo;&nbsp;binary&nbsp;&raquo; );</p>
<p>// on ferme le socket<br />
socket.end();</p>
<p>});</p>
<p>});</p>
<p>}).listen( 843 );<br />
[/javascript]</p>
<p>On sauve ça dans un fichier .js, par exemple node-security.js. On crée ensuite dans le même dossier, le fichier policy.xml qui contient notre policy d’accès, ici on autorise uniquement le port 8888, sur lequel tournera notre serveur de jeu :</p>
<p>Puis on lance le serveur en tâche de fond :</p>
<p><code>$ sudo node node-security.js &amp;<br />
[1] 2740<br />
</code></p>
<p>On peut maintenant interroger le serveur depuis une autre machine à travers Internet, en utilisant l’utilitaire de bas-niveau Telnet, par exemple sous Windows, Démarrer &gt; Exécuter &gt; telnet :</p>
<p><code>Microsoft Telnet&gt; o 88.12.xx.xx 843<br />
</code></p>
<p>On voit s’afficher notre policy file, puis le socket se fermer : notre .swf est maintenant autorisé à continuer.</p>
<p>Pour stopper le serveur, on tue le process par son PID :</p>
<p><code>$ sudo kill 2740<br />
</code></p>
<p>Ce serveur de policy file devra tourner en tâche de fond durant tout le reste du tutoriel.</p>
<h2>Dans la pratique : une application de chat</h2>
<h3>1) côté serveur</h3>
<p>Nous voilà prêts désormais pour développer un serveur de chat basique. Pour l&#8217;instant, nous pouvons ouvrir un socket par client, puis y envoyer des données :</p>
<p>[javascript]<br />
net.createServer( function (socket) {<br />
socket.write(&#8216;foo&#8217;);<br />
});<br />
[/javascript]</p>
<p>Nous allons avoir besoin de recevoir des données : le principe d&#8217;un serveur de chat est le suivant :</p>
<ul>
<li>le serveur reçoit une donnée d&#8217;un client,</li>
<li>il renvoie cette donnée vers tous les clients connectés.</li>
</ul>
<p>[javascript]<br />
net.createServer( function (socket) {</p>
<p>socket.write(&#8216;foo&#8217;);<br />
socket.on( &laquo;&nbsp;data&nbsp;&raquo;, function (data) {</p>
<p>// traitement des données lues</p>
<p>});</p>
<p>});<br />
[/javascript]</p>
<p>On constate ici la similitude avec l&#8217;Actionscript : on traite un évenement par un handler <em>on( ,  )</em>.</p>
<p>Nous aurons ici 2 types de messages : <strong>/join</strong> à la connexion, <strong>/msg</strong> lorsque c&#8217;est un message à afficher. Le tout est véhiculé sous forme de chaîne UTF-8.</p>
<p>[javascript]<br />
net.createServer( function (socket) {</p>
<p>socket.setEncoding(&#8216;utf8&#8242;);<br />
socket.on( &laquo;&nbsp;data&nbsp;&raquo;, function (data) {</p>
<p>if( data.substring( 0, 5 ) == &#8216;/join&#8217; ) {</p>
<p>// connexion d&#8217;un nouvel utilisateur<br />
// &#8230;</p>
<p>}</p>
<p>if( data.substring( 0, 5 ) == &#8216;/msg&#8217; ) {</p>
<p>// message à renvoyer à tous les utilisateurs<br />
// &#8230;</p>
<p>}</p>
<p>});</p>
<p>});<br />
[/javascript]</p>
<p>Il nous reste à garder une liste des sockets ouverts, afin de pouvoir leur dispatcher les messages reçus. On utilisera un simple <em>Object {&#8230;}</em>, qui fonctionne de la même manière en JS et en AS.</p>
<h4><span style="color: #444444; line-height: 24px;">Voici le code complet du serveur, sauvons-le sous <em>node-chat-server.js</em></span></h4>
<p>[javascript]<br />
// importation des modules necessaires<br />
var net = require(&#8216;net&#8217;);<br />
var sessions = {};</p>
<p>// creation du serveur<br />
var server = net.createServer( function (socket) {</p>
<p>socket.setEncoding(&#8216;utf8&#8242;);</p>
<p>// handler de données entrantes<br />
socket.on( &laquo;&nbsp;data&nbsp;&raquo;, function (data) {</p>
<p>// l&#8217;utilisateur rejoint pour la 1ère fois<br />
if( data.substring( 0, 5 ) == &#8216;/join&#8217; ) {</p>
<p>// on loggue côté serveur<br />
console.log(&#8216;[join] &#8216; + data.substring( 6, data.length ) );</p>
<p>// on stocke une référence vers le socket dans l&#8217;objet<br />
var session = { socket: socket,<br />
nick: data.substring( 6, data.length ) };<br />
sessions[ Math.floor( Math.random() * 99999999999 ).toString() ] = session;<br />
console.log(sessions);</p>
<p>}</p>
<p>// l&#8217;utilisateur envoie un message<br />
if( data.substring( 0, 4 ) == &#8216;/msg&#8217; ) {</p>
<p>// on loggue côté serveur<br />
console.log(&#8216;[msg] &#8216; + data.substring( 5, data.length ) );</p>
<p>// on parcourt l&#8217;objet  à la recherche du nickname correspondant<br />
for ( var i in sessions ) {<br />
var session = sessions[i];<br />
if ( session.socket === socket ) {<br />
var nick = session.nick;<br />
}<br />
}</p>
<p>// on parcourt l&#8217;objet  et envoie le msg à tous les sockets<br />
for ( var i in sessions ) {<br />
sessions[i].socket.write( nick + &#8216; : &#8216; + data.substring( 5, data.length ) );<br />
}<br />
}<br />
});</p>
<p>// handler de déconnexion<br />
socket.on(&laquo;&nbsp;end&nbsp;&raquo;, function () {</p>
<p>// on supprime la référence vers le socket dans l&#8217;objet<br />
for ( var i in sessions ) {<br />
var session = sessions[i];<br />
if ( session.socket === socket ) {<br />
socket.end();<br />
delete sessions[i];<br />
}<br />
}</p>
<p>});</p>
<p>}).listen( 8888 );<br />
[/javascript]</p>
<h3>2) côté client</h3>
<p>Il nous reste à développer un petit client de chat en AS3, qui aura un rôle très limité :</p>
<ul>
<li>Envoyer<strong> /join</strong> dans le socket à la connexion de l&#8217;utilisateur,</li>
<li>Envoyer <strong>/msg</strong> dans le socket au clic sur le bouton ,</li>
<li>Afficher dans une zone de texte les messages reçus du serveur.</li>
</ul>
<p>Commençons par instancier un Socket :</p>
<p><span style="font-size: 13px; font-family: Monaco, Consolas, 'Andale Mono', 'DejaVu Sans Mono', monospace; line-height: 19px;"> </span></p>
<p>[as3]<br />
// creation du socket<br />
_socket = new Socket();<br />
_socket.addEventListener( Event.CONNECT, onConnect );<br />
_socket.addEventListener( ProgressEvent.SOCKET_DATA, onData );<br />
&#8230;<br />
[/as3]</p>
<p>Nous écoutons principalement 2 évenements :</p>
<ul>
<li><strong>onConnect :</strong> émis lorsque la connexion au serveur a été initiée sans encombre.</li>
<li><strong>onData :</strong> émis à la réception d&#8217;une donnée depuis le serveur.</li>
</ul>
<h3>2.a) l&#8217;envoi de données</h3>
<p>[as3]<br />
_socket.writeUTFBytes( &#8216;/msg &#8216; + _msgInput.text );<br />
_socket.flush();<br />
[/as3]<br />
La première méthode est assez claire d&#8217;elle-même, on écrit dans le socket une chaîne UTF-8. Attention à bien appeler la méthode Socket.flush(), qui force l&#8217;envoi effectif des données (sinon, il ne se passera rien).</p>
<h3>2.b) la réception de données</h3>
<p>[as3]<br />
var read : String = _socket.readUTFBytes(_socket.bytesAvailable);<br />
[/as3]<br />
C&#8217;est cette méthode qui permet de lire des  bytes depuis un Socket, et les converti en String à la volée</p>
<h3>2.c) la source compléte du client Flash</h3>
<p>[as3]<br />
package<br />
{<br />
import com.carlcalderon.arthropod.Debug;</p>
<p>import com.bit101.components.TextArea;<br />
import com.bit101.components.InputText;<br />
import com.bit101.components.Text;<br />
import com.bit101.components.PushButton;<br />
import com.bit101.components.Label;</p>
<p>import flash.display.Sprite;<br />
import flash.net.Socket;<br />
import flash.events.Event;<br />
import flash.events.IOErrorEvent;<br />
import flash.events.SecurityErrorEvent;<br />
import flash.events.ProgressEvent;<br />
import flash.events.MouseEvent;</p>
<p>/**<br />
* &#8230;<br />
* @author<br />
*/<br />
public class Application extends Sprite<br />
{<br />
private var _loginInput : InputText;<br />
private var _loginButton : PushButton;<br />
private var _sendButton : PushButton;<br />
private var _msgInput : InputText;<br />
private var _msgWindow : TextArea;</p>
<p>private var _login : String;</p>
<p>private var _socket : Socket;</p>
<p>public function Application() : void<br />
{<br />
Debug.clear();</p>
<p>// creation de l&#8217;U.I.<br />
_loginInput = new InputText( this, 150, 250, &laquo;&nbsp;guest&nbsp;&raquo; + Math.floor( Math.random() * 10000 ) );<br />
_loginButton = new PushButton( this, 350, 250, &laquo;&nbsp;connexion&nbsp;&raquo;, onLogin );</p>
<p>// creation du socket<br />
_socket = new Socket();</p>
<p>_socket.addEventListener( Event.CLOSE, onClose );<br />
_socket.addEventListener( Event.CONNECT, onConnect );<br />
_socket.addEventListener( IOErrorEvent.IO_ERROR, onError );<br />
_socket.addEventListener( SecurityErrorEvent.SECURITY_ERROR, onSecurityError );<br />
_socket.addEventListener( ProgressEvent.SOCKET_DATA, onData );<br />
}</p>
<p>private function onLogin( e : MouseEvent ) : void<br />
{<br />
// suppression de l&#8217;U.I. de login<br />
removeChild(_loginInput);<br />
removeChild(_loginButton);</p>
<p>_login = _loginInput.text;</p>
<p>_loginInput = null;<br />
_loginButton = null;</p>
<p>// creation de l&#8217;U.I. du chat<br />
_msgWindow = new TextArea( this, 5, 5 );<br />
_msgWindow.width = 780;<br />
_msgWindow.height = 550;<br />
_msgInput = new InputText( this, 5, 570, &laquo;&nbsp;taper un message ici&nbsp;&raquo; );<br />
_msgInput.width = 670;<br />
_sendButton = new PushButton( this, 680, 570, &laquo;&nbsp;envoyer&nbsp;&raquo;, onSend );</p>
<p>// connexion au serveur<br />
_socket.connect( &#8216;xxx.xxx.109.181&#8242;, 8888 );<br />
}</p>
<p>// envoi d&#8217;un message suite à un clic utilisateur<br />
private function onSend( e : MouseEvent) : void<br />
{<br />
_socket.writeUTFBytes( &#8216;/msg &#8216; + _msgInput.text );<br />
_socket.flush();<br />
_msgInput.text = &nbsp;&raquo;;<br />
}</p>
<p>private function onClose( e : Event ) : void<br />
{<br />
Debug.log( &#8216;onClose&#8217; );<br />
}</p>
<p>// connexion au socket effectuée<br />
private function onConnect( e : Event ) : void<br />
{<br />
Debug.log( &#8216;onConnect&#8217; );</p>
<p>_socket.writeUTFBytes( &#8216;/join &#8216; + _login );<br />
_socket.flush();<br />
}</p>
<p>private function onError( e : IOErrorEvent ) : void<br />
{<br />
Debug.error( &#8216;onError&#8217; );<br />
}</p>
<p>private function onSecurityError( e : SecurityErrorEvent ) : void<br />
{<br />
Debug.error( &#8216;onSecurityError&#8217; );<br />
}</p>
<p>// réception de données depuis le socket<br />
private function onData( e : ProgressEvent ) : void<br />
{<br />
Debug.log( &#8216;onData&#8217; );<br />
_msgWindow.text += _socket.readUTFBytes(_socket.bytesAvailable) + &laquo;&nbsp;\n&nbsp;&raquo;;<br />
}</p>
<p>}</p>
<p>}<br />
[/as3]</p>
<h2>Conclusion</h2>
<p>On lance maintenant le serveur via les commandes habituelles :</p>
<p><code>$ node node-chat-server.js &amp;<br />
</code></p>
<p>On lance ensuite le client Flash depuis une autre machine, et voilà, ça marche.</p>
<p>En à peine quelques lignes de code Javascript simple et basique, voilà un serveur de chat rapide et efficace. A partir de cette base, voici quelques améliorations possibles et pistes à creuser :</p>
<p>• Les sources d&#8217;un serveur de chat complet par l&#8217;auteur de node.js sont une mine d&#8217;or : <a href="http://github.com/ry/node_chat" target="_self">node_chat sur GitHub</a>.</p>
<p>• les messages sont encodés sous forme de chaînes UTF-8. C&#8217;est lourd et lent. Pour un jeu online, il serait préférable d&#8217;utiliser un format binaire. En supprimant la ligne <em>socket.setEncoding(&#8216;utf8&#8242;);</em>, l&#8217;argument <strong>data</strong> reçu ne sera plus une chaîne mais un objet Buffer, particulier à node.js.</p>
<p>• un serveur de jeu online doit pouvoir communiquer avec une base de données. Sachant que MySQL tourne sur socket (mysql.sock), il est relativement simple de communiquer avec.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.entremidietdeux.com/creer-une-application-multi-utilisateurs-avec-as3-et-node-js/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

