as3 How to i solve auto reconnect with settimeout? - arrays

I want to connect my url and port from my vector list. I use setTimeout in my reconnect function but my problem is when i use settimeout and click reconnect button settimout is ask 5 times for connection and when connecttion succes after connection is down because settimeout is continue send the request my list for connect and states is broken.
How i solve this problem or have any different way ?
public static function reconnect(serverUrl:String = "", serverPort:int = 0):void {
var nextUri: SocketConnection = urisToTry.next;
test = setTimeout(reconnect, 4000, nextUri.host, nextUri.port);
}
and this is my iteration class
public class UriIterator
{
public var _availableAddresses: Vector.<SocketConnection> = new Vector.<SocketConnection>();
public var currentIndex:int = 0;
public function UriIterator(){
}
public function withAddress(host: String, port: int): UriIterator {
const a: SocketConnection = new SocketConnection(host, port);
_availableAddresses.push(a);
return this;
}
public function get next():SocketConnection {
var address = _availableAddresses[currentIndex];
currentIndex++;
if (currentIndex > _availableAddresses.length - 1)
currentIndex = 0;
return address;
}
}
my list
const urisToTry: UriIterator = new UriIterator()
.withAddress("http://urlone.com", 1211)
.withAddress("http://urltwo.com", 1212)
.withAddress("http://urlthree.com", 1213)
.withAddress("http://urlfour.com", 1214)
...

First, simplify and clean what you have there.
public class UriIterator
{
// You won't gain literally anything from using strong-typed Vector.
// Long variable names make your code harder to read. Use simple ones.
// Another good idea is to make these members private
// so no one else will mess with them from the outside.
private var list:Aray = new Array;
private var index:int = 0;
// If constructor does nothing you might as well omit it.
// It is not prohibited to do it the way you did,
// yet again no gain for unnecessary complications.
public function append(host:String, port:int):void
{
list.push(new SocketConnection(host, port));
}
public function get next():SocketConnection
{
var result:SocketConnection = list[index];
if (++index >= list.length) index = 0;
return result;
}
}
Then.
private var uriList:UriIterator;
uriList = new UriIterator;
uriList.append("http://urlone.com", 1211);
uriList.append("http://urltwo.com", 1212);
uriList.append("http://urlthree.com", 1213);
uriList.append("http://urlfour.com", 1214);
Finally.
private var recId:uint;
// Event handler for refused connection or disconnected socket.
private function onDisconnect(e:Event):void
{
// Dispose of existing/failed connection objects first.
// #TODO
// Just in case.
clearTimeout(recId);
// Now, lets schedule the next connection in 4 seconds.
recId = setTimeout(onReconnect, 4000);
}
private function onReconnect():void
{
// Get the next connection.
var aNext:SocketConnection = uriList.next;
// Put your connection routines here.
// #TODO
}

Related

AS3 - How to get next array Index and how i change my Function

i have connectAgain function when connection is lose i addchild button, when i click this button call connectAgain function in this function have serverUrl and serverPort only one ip and port this is current system and its work.
But now, i have 4 or 5 ip and port. i guess i add array or dictionary or Object, and when connection is lose i click button and call ip and port inside the array but every connection lose i want to call next ip and port not same ip and port.
please help me thanks this is my current function. How can i do it ?
public static function connectAgain(serverUrl:String = "", serverPort:int = 0):void {
serverUrl = myGameClass.serverUrl;
serverPort = myGameClass.serverPort;
}
You can use some helper classes as follows:
class UriIterator {
private var _availableAddresses: Vector.<UriVO> = new Vector.<UriVO>();
public function withAddress(host: String, port: int): UriIterator {
const a: UriVO = new UriVO(host, port);
_availableAddresses.push(a);
return this;
}
public function get next(): UriVO
{
return _availableAddresses.length ? _availableAddresses.pop() : null;
}
}
class UriVO {
private var _host: String;
private var _port: int;
public function Address(host: String, port: int) {
_host = host;
_port = port;
}
public function get host():String {
return _host;
}
public function get port():int {
return _port;
}
}
Somewhere on init you create the iterator:
...
const urisToTry: UriIterator = new UriIterator()
.withAddress("http://urlone.com", 1211)
.withAddress("http://urltwo.com", 1212)
.withAddress("http://urlthree.com", 1213)
.withAddress("http://urlfour.com", 1214)
...
Then, in reconnect function you can call the next() function to retrieve next url for connection:
const nextUri: UriVO = urisToTry.next;
if (nextUri)
connectAgain(nextUri.host, nextUri.port);
else
// you've tried all uris and connection failed.

actionscript 3 load URL from array

I'm making a mp3/steaming radio with MVC, where I'm trying to load an URL from a array.
I have a radio class:
public class Radio
{
private var titel:String;
private var url:URLRequest;
private var cover:Bitmap;
public function Radio(titel:String, url:URLRequest, cover:Bitmap)
{
this.titel = titel;
this.url = url;
this.cover = cover;
}
public function getTitel():String {
return titel;
}
public function getURL():URLRequest {
return url;
}
public function getCover():Bitmap {
return cover
}
}
In the controller i have this:
public function selectRadio(radio:Radio):void{
model.selectRadio(radio);
}
In view I have the button with the eventlistner:
radio.addEventListener(MouseEvent.CLICK, function():void {
controller.selectRadio(model.getRadio(0));
});
And finally in the model i have:
private var radio:Radio = new Radio("P3", new URLRequest("http://live-icy.gss.dr.dk:80/A/A05L.mp3"), drp3);
private var radioArray:Array = new Array(radio);
private var r:Number;
public function selectRadio(radio:Radio):void {
var s:Sound = new Sound();
var chan:SoundChannel = s.play();
s.load();
trace("radio");
}
public function getRadios():Array {
return radioArray;
trace("All radio channels collected");
}
public function getRadio(radioNumber:int):Radio {
r = radioNumber;
return radioArray[radioNumber];
trace("Actual radio collected");
}
The problem is in the selectRadio function. I don't know how to load the URL in the arrays. It should be s.load(--something in here--); The reason why I'm doing this, is because I want to have multiple radio stations.
Hope you can help :)
var s:Sound = new Sound(radio.getURL());
var chan:SoundChannel = s.play();
load function will be called automatically by the constructor, also don't forget to stop previous SoundChannel.
and here: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Sound.html you will find everything about Sound

AS3 array of movieclip buttons with fluid animation and AT state

I wanted to setup an array of movieclip buttons to navigate across my timeline via labels, this is where it went pear shaped.
Having spent three days reading and attempting most online solutions I couldn't find a method which worked 100% without failing in some way or another.
I've had some joy with the method below having seen a blog entry covering different ways to call frames etc and which highlighted the bugbear below :
clipArray[i].mouseChildren = false; //Hidden bugbear
I've added the full code below so hopefully it may help anyone else who similarly nearly resorted to hari-kari in trying this.
import flash.events.MouseEvent;
import flash.events.Event;
var clipArray:Array = [btn_1,btn_2]; // Movieclip's called btn_1 etc...
var destArray:Array = ["page_1","page_2"]; Labels on timeline...
for (var i:int = 0; i < clipArray.length; i++) {
clipArray[i].buttonMode = true; // Define Button from Movie Clip
clipArray[i].useHandCursor = true; // Enable HandCursor over clip
clipArray[i].mouseChildren = false; // Define clip as single denomination
clipArray[i].addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
clipArray[i].addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
clipArray[i].addEventListener(Event.ENTER_FRAME, frameHandler);
clipArray[i].addEventListener(MouseEvent.CLICK,clickHandler, false, 0, true);
}
function clickHandler(event:MouseEvent):void {
for (var i:int = 0; i < clipArray.length; i++) {
if (event.currentTarget == clipArray[i]) {
this.gotoAndStop(destArray[i]);
clipArray[i].mouseEnabled = false;
clipArray[i].useHandCursor = false;
clipArray[i].alpha = 0.5;
} else {
clipArray[i].mouseEnabled = true;
clipArray[i].useHandCursor = true;
clipArray[i].alpha = 1;
}
}
}
function mouseOverHandler(e:MouseEvent){
e.target.onOff = true;
}
function mouseOutHandler(e:MouseEvent){
e.target.onOff = false;
}
function frameHandler(e:Event){
if(e.target.onOff){
e.target.nextFrame();
} else {
e.target.prevFrame();
}
}
This works fine, now however my understanding of whether it is 'good' code or not is an issue, if this could be improved in any way I'd like to know why and how as the problem with learning AS3 from 2 is that often you use code having seen it online without fully grasping the detail.
Tentatively, I'm pleased as this proved to be a nightmare to find or to resolve and hope it helps anyone else in a similar state of mind.
Adding MovieClip buttons with fluidity and which cancel out from an array became a three day mission when you're learning...
You might find you have more freedom if you put all of this in a class and use the Tween class to travel to your 'labels' instead of the timeline. It would mean that you would be able to remove your event listeners until your animation has finished.
Also it looks like you might be better off using the MouseEvent.ROLL_OVER and MouseEvent.ROLL_OUT
If I'm honest I don't know what some of your methods want to do without seeing your whole project, but I've quickly written a class for you. I've replaced animating between pages with just having your buttons animate to a ramdom location. (Remember to export your MovieClips you create with the IDE into actionscript, giving them the class name Button01, Button02 etc...). I hope this sends you in the right direction :)
package com
{
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
import fl.transitions.Tween;
import fl.transitions.easing.*;
import fl.transitions.TweenEvent;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
public class Main extends MovieClip
{
private var btn_1:Button01 = new Button01;
private var btn_2:Button02 = new Button02;
private var clipArray:Array = new Array();
private var xAxis:Number = 0;
private var yAxis:Number = 0;
private static const WIDTH:int = 300;
private static const HEIGHT:int = 250;
private var tweenButton:Tween;
public function Main()
{
makeArrays();
addChildren();
setEventListeners()
}
private function makeArrays():void
{
for(var i:uint=0; i<2; i++)
clipArray.push(this['btn_'+(i+1)]);
}
private function addChildren():void
{
for(var i:uint=0; i<clipArray.length; i++){
addChild(clipArray[i]);
clipArray[i].x = WIDTH*Math.random();
clipArray[i].y = HEIGHT*Math.random();
}
}
private function setEventListeners():void
{
for (var i:uint=0; i<clipArray.length; i++) {
clipArray[i].buttonMode = true; // Define Button from Movie Clip
//clipArray[i].useHandCursor = true; // Enable HandCursor over clip
//clipArray[i].mouseChildren = false; // Define clip as single denomination // DON'T NEED THIS WITH ROLL_OVER
clipArray[i].addEventListener(MouseEvent.ROLL_OVER, mouseOverHandler);
clipArray[i].addEventListener(MouseEvent.ROLL_OUT, mouseOutHandler);
clipArray[i].addEventListener(Event.ENTER_FRAME, frameHandler);
clipArray[i].addEventListener(MouseEvent.CLICK, clickHandler, false, 0, true);
}
}
private function tweenButtons(e:Event):void
{
var dispObj = e.currentTarget as DisplayObject;
dispObj.removeEventListener(MouseEvent.CLICK, clickHandler)
tweenButton = new Tween(dispObj, 'x', Regular.easeIn, dispObj.x, WIDTH*Math.random(), 1, true);
tweenButton = new Tween(dispObj, 'y', Regular.easeIn, dispObj.y, HEIGHT*Math.random(), 1, true);
tweenButton.addEventListener(TweenEvent.MOTION_FINISH, reattachEventListener);
}
private function reattachEventListener(e:Event):void
{
tweenButton.removeEventListener(TweenEvent.MOTION_FINISH, reattachEventListener);
for (var i:uint=0; i<clipArray.length; i++) {
if(!(hasEventListener(MouseEvent.CLICK)))
clipArray[i].addEventListener(MouseEvent.CLICK, clickHandler, false, 0, true);
}
}
private function clickHandler(e:MouseEvent):void
{
for (var i:uint=0; i<clipArray.length; i++) {
if (e.currentTarget == clipArray[i]) {
tweenButtons(e);
clipArray[i].buttonMode = false
clipArray[i].alpha = 0.5;
} else {
clipArray[i].buttonMode = true;
clipArray[i].alpha = 1;
}
}
}
private function mouseOverHandler(e:MouseEvent):void // I HAVE NO IDEA WHAT YOU'RE TRYING TO DO HERE
{
e.target.onOff = true;
}
private function mouseOutHandler(e:MouseEvent):void // I HAVE NO IDEA WHAT YOU'RE TRYING TO DO HERE
{
e.target.onOff = false;
}
private function frameHandler(e:Event):void // I HAVE NO IDEA WHAT YOU'RE TRYING TO DO HERE
{
if(e.target.onOff){
e.target.nextFrame();
}else{
e.target.prevFrame();
}
}
}
}

How to receive DialogResult using mvvm-light Messenger

I'm trying to use the mvvm-light messenger capability to open a custom confirm password dialog in my view, triggered by a command in my viewmodel.
I think I understand the usage of Messenger.Default.Register and Messenger.Default.Send.
But how do I get the dialog results back in my viewmodel?
To me the sending seems to be a one way street...
Could someone help a beginner with a small C#/WPF code sample?
Thanks for any help
IMHO it is better to use the NotificationMessageAction<T> as it is cut out for this task.
On the sender side:
var msg = new NotificationMessageAction<MessageBoxResult>(this, "GetPassword", (r) =>
{
if (r == MessageBoxResult.OK)
{
// do stuff
}
});
Messenger.Default.Send(msg);
And on the receiver side:
Messenger.Default.Register<NotificationMessageAction<MessageBoxResult>>(this, (m) =>
{
if (m.Notification == "GetPassword") {
var dlg = new PasswordDialog();
var result = dlg.ShowDialog();
m.Execute(result);
}
});
I believe that this approach is cleaner as it does not create an unnecessary dependency from the View to the ViewModel (although this way round is not so bad). For better readability consider sub-classing the NodificationMessageAction<MessageResult>. I.e.
public class ShowPasswordMessage : NotificationMessageAction<MessageBoxResult>
{
public ShowPasswordMessage(object Sender, Action<MessageBoxResult> callback)
: base(sender, "GetPassword", callback)
{
}
}
Then the sender
var msg = new ShowPasswordMessage(this, (r) =>
{
if (r == MessageBoxResult.OK)
{
// do stuff
}
});
Messenger.Default.Send(msg);
and receiver side
Messenger.Default.Register<ShowPasswordMessage>(this, (m) =>
{
var dlg = new PasswordDialog();
var result = dlg.ShowDialog();
m.Execute(result);
});
becomes a lot clearer.
And verry important unregister the recipient as else you might create a memory leak.
In Register method you can show a dialog and pass the YourViewModel reference.
Messenger.Default.Register<YourViewModel>(this, "showDialog", viewModel=>
{
var dlg = new Dialog();
viewModel.Result = dlg.ShowDialog();
});
somewhere in your code you can throw Send() message with a reference to YourViewModel like this:
Messenger.Default.Send(viewModel, "showDialog");
In order to achieve the above using DialogMessage as the title suggests,
one may use the following:
sender side:
void SendMessage(String msgText)
{
DialogMessage messege = new DialogMessage(msgText, res =>
{
callback(res);
})
//set more dialog properties using the Initializer
{ Button = MessageBoxButton.OKCancel, Caption = "" };
Messenger.Default.Send(messege, "mb1");
}
public void callback(MessageBoxResult res)
{
if (res == MessageBoxResult.OK)
{ /*do something*/ }
}
receiver side:
void Rec()
{
Messenger.Default.Register<DialogMessage>(
this, "mb1", msg => ShowDialog(msg));
Unloaded += Unreg;
}
void ShowDialog(DialogMessage msg)
{
var result = MessageBox.Show(
msg.Content,
msg.Caption,
msg.Button);
msg.Callback(result);
}
note the explicit call to the Callback method in the last line of the receiver.
msg.Callback(result);

Silverlight Async Method Chaining (Possible gotchas?)

I am working on a 'proof of concept' Silverlight 4 project and am learning the way of THE ASYNC. I have stopped fighting the urge to implement some pseudo-synchronous smoke and mirrors technique. I am going to learn to stop worrying and love THE ASYNC.
Most of the time I just use a BusyIndicator while async methods are running and all is good but I have run into a few situations where I need to call methods sequentially. I put together this example and it works. But in my experience... if it works... there is something wrong with it.
When is this going to blow up in my face or steal my wife or date one of my daughters?
Is there a better way to do this?
The Code:
public class CustomPage : Page
{
static readonly object _AsyncMethodChain_Lock = new object();
private Dictionary<Action<object>, string> _AsyncMethodChain = new Dictionary<Action<object>, string>();
public Dictionary<Action<object>, string> AsyncMethodChain
{
get { lock (_AsyncMethodChain_Lock) { return this._AsyncMethodChain; } }
set { lock (_AsyncMethodChain_Lock) { this._AsyncMethodChain = value; } }
}
private void CustomPage_Loaded(object sender, RoutedEventArgs e)
{
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
var user = this.SecurityProvider.UserObject as TimeKeeper.UserServiceReference.User;
if (user == null)
return;
this.AsyncMethodChain.Add(
data =>
{
var userServiceClient = new UserServiceClient();
userServiceClient.GetCompleted +=
(send, arg) =>
{
var userViewSource = this.Resources["userViewSource"] as CollectionViewSource;
userViewSource.Source = new List<UserServiceReference.User>(new UserServiceReference.User[1] { arg.Result });
userViewSource.View.MoveCurrentToPosition(0);
this.AsyncMethodChain.ExecuteNext(arg.Result.UserID, this.BusyIndicator);
};
userServiceClient.GetAsync(user.UserID);
},
"Loading user..."
);
this.AsyncMethodChain.Add(
data =>
{
var userID = (int)data;
var timeLogServiceClient = new TimeLogServiceClient();
timeLogServiceClient.FindByUserIDCompleted +=
(send, arg) =>
{
var timeLogViewSource = this.Resources["timeLogViewSource"] as CollectionViewSource;
timeLogViewSource.Source = arg.Result;
this.AsyncMethodChain.ExecuteNext(null, this.BusyIndicator);
};
timeLogServiceClient.FindByUserIDAsync(userID);
},
"Loading time logs..."
);
this.AsyncMethodChain.ExecuteNext(null, this.BusyIndicator);
}
}
}
public static class Extensions
{
public static void ExecuteNext(this Dictionary<Action<object>, string> methods, object data, BusyIndicator busyIndicator)
{
if (methods.Count <= 0)
{
busyIndicator.BusyContent = "";
busyIndicator.IsBusy = false;
return;
}
else
{
var method = methods.Keys.ToList<Action<object>>()[0];
busyIndicator.BusyContent = methods[method];
busyIndicator.IsBusy = true;
}
methods.ExecuteNext(data);
}
public static void ExecuteNext(this Dictionary<Action<object>, string> methods, object data)
{
var method = methods.Keys.ToList<Action<object>>()[0];
methods.Remove(method);
method(data);
}
}
What you have sine looks pretty good, but if you are still worried about the callsequence i would sugest that you create a new method in your webservice which will call the other four in whatever sequence you need them to and call this new method from your silverlight application.
I dont see the need for you to do that as your current implemenation is pretty good as well.

Resources