blob: 574af9325325398aa8cbaa2eaba77c3f62a406da [file] [log] [blame]
<?xml version="1.0" encoding="utf-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Author: Christophe Coenraets - http://coenraets.org -->
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:parsley="http://www.spicefactory.org/parsley"
xmlns:controls="flex.skeletons.traderdesktop.controls.*"
width="100%" height="100%">
<fx:Script>
<![CDATA[
import flex.skeletons.traderdesktop.model.Stock;
import flex.skeletons.traderdesktop.skins.SidebarPanelSkin;
import mx.messaging.AdvancedChannelSet;
import mx.messaging.Channel;
import mx.messaging.channels.AMFChannel;
import mx.messaging.channels.RTMPChannel;
import mx.messaging.channels.StreamingAMFChannel;
import mx.messaging.events.MessageEvent;
import mx.rpc.AsyncResponder;
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.RemoteObject;
protected var streamingAMFChannel:StreamingAMFChannel;
protected var rtmpChannel:RTMPChannel;
protected var amfChannel:AMFChannel;
[Bindable]
protected var stockList:ArrayCollection;
protected var stockMap:Dictionary;
private var _feedService:RemoteObject;
[Inject]
public function set channels(channels:ArrayCollection):void
{
comboChannels.dataProvider = channels;
if (channels && channels.length>0)
{
comboChannels.selectedIndex = 0;
}
}
[Inject]
public function set feedService(service:RemoteObject):void
{
_feedService = service;
getFeedStatus();
}
public function get feedService():RemoteObject
{
return _feedService;
}
protected var msgCount:int=0;
protected var totalLatency:Number = 0;
protected function messageHandler(event:MessageEvent):void
{
msgCount++;
var latency:Number = new Date().time - event.message.timestamp;
totalLatency += latency;
latencyLbl.text = ""+latency;
var changedStock:Stock = event.message.body as Stock;
var stock:Stock = stockMap[changedStock.symbol];
if (stock)
{
stock.open = changedStock.open;
stock.change = changedStock.change;
stock.last = changedStock.last;
stock.high = changedStock.high;
stock.low = changedStock.low;
stock.date = changedStock.date;
}
}
public function get targetFrameRate():int
{
return nsFrameRate.value;
}
protected function getFeedStatus():void
{
var token:AsyncToken = feedService.getFeedStatus();
token.addResponder(new AsyncResponder(getFeedStatus_result, getFeedStatus_fault));
}
protected function startFeed():void
{
var token:AsyncToken = feedService.start(nsNumStocks.value, nsNumThreads.value, nsSleepMillis.value);
token.addResponder(new AsyncResponder(startFeed_result, startFeed_fault));
}
protected function stopFeed():void
{
if (consumer.subscriptions.length>0)
unsubscribe();
var token:AsyncToken = feedService.stop();
token.addResponder(new AsyncResponder(stopFeed_result, stopFeed_fault));
stockList = null;
}
protected function getStocks():void
{
var token:AsyncToken = feedService.getStocks();
token.addResponder(new AsyncResponder(getStocks_result, getStocks_fault));
}
protected function getFeedStatus_result(event:ResultEvent, token:AsyncToken):void
{
var status:Object = event.result;
if (status.running)
{
log("The server feed is running");
log("Stocks: " + status.numStocks + " Threads: " + status.numThreads + " sleepTime: " + status.sleepTime);
nsNumStocks.value = status.numStocks;
nsNumThreads.value = status.numThreads;
nsSleepMillis.value = status.sleepTime;
getStocks();
}
else
{
log("The server feed is not running");
}
}
protected function getFeedStatus_fault(event:FaultEvent, token:AsyncToken):void
{
log("Make sure you started the server.\n" + event.fault.faultString);
}
protected function startFeed_result(event:ResultEvent, token:AsyncToken):void
{
log("Feed started successfully");
getStocks();
}
protected function startFeed_fault(event:FaultEvent, token:AsyncToken):void
{
log("The request to start the feed failed.\n" + event.fault.faultString);
}
protected function stopFeed_result(event:ResultEvent, token:AsyncToken):void
{
log("Feed stopped successfully");
}
protected function stopFeed_fault(event:FaultEvent, token:AsyncToken):void
{
log("The request to stop the feed failed.\n" + event.fault.faultString);
}
protected function getStocks_result(event:ResultEvent, token:AsyncToken):void
{
stockList = event.result as ArrayCollection;
stockMap = new Dictionary();
var stock:Stock;
for (var i:int=0; i<stockList.length; i++)
{
stock = stockList.getItemAt(i) as Stock;
stockMap[stock.symbol] = stock;
}
if (cbRender.selected)
dg.dataProvider = stockList;
}
protected function getStocks_fault(event:FaultEvent, token:AsyncToken):void
{
log("Error: " + event.fault.faultString);
}
protected function startBenchmark():void
{
var numStocks:int = nsNumStocks.value;
var numThreads:int = nsNumThreads.value;
var sleepMillis:int = nsSleepMillis.value;
var timer:Timer = new Timer(60000, 1);
var benchmark:Object = {
stocks: numStocks,
threads: numThreads,
sleep: sleepMillis,
throughput: nf1.format(numThreads*1000/sleepMillis),
channel: comboChannels.selectedItem.id,
maxFrequency: nsMaxClientFrequency.value,
frameRate: nsFrameRate.value,
rendering: cbRender.selected,
messageCount: "...",
mps: "...",
latency: "..."};
benchmarks.addItem(benchmark);
timer.addEventListener(TimerEvent.TIMER_COMPLETE,
function():void
{
benchmark.messageCount = nf0.format(msgCount);
benchmark.mps = nf1.format(msgCount / 60);
benchmark.latency = nf1.format(totalLatency / msgCount);
dgPerf.dataProvider = benchmarks;
});
msgCount = 0;
totalLatency = 0;
timer.start();
}
protected function subscribe():void
{
stage.frameRate = nsFrameRate.value;
var cs:AdvancedChannelSet = new AdvancedChannelSet();
cs.addChannel(comboChannels.selectedItem as Channel);
consumer.channelSet = cs;
consumer.maxFrequency = nsMaxClientFrequency.value;
addSubscriptions();
consumer.subscribe();
}
protected function unsubscribe():void
{
var stock:Stock;
for (var i:int=0; i<stockList.length; i++)
{
stock = stockList.getItemAt(i) as Stock;
consumer.removeSubscription(stock.symbol);
}
log("Unsubscribed from " + stockList.length + " stocks");
consumer.unsubscribe();
consumer.disconnect();
}
protected function addSubscriptions():void
{
var stock:Stock;
for (var i:int=0; i<stockList.length; i++)
{
stock = stockList.getItemAt(i) as Stock;
consumer.addSubscription(stock.symbol);
}
log("Subscribed to " + stockList.length + " stocks");
}
public function closeConnection():void
{
consumer.unsubscribe();
consumer.disconnect();
}
protected function log(message:String):void
{
logArea.text += message + "\n";
}
]]>
</fx:Script>
<fx:Declarations>
<parsley:Configure/>
<mx:NumberFormatter id="nf0" precision="0"/>
<mx:NumberFormatter id="nf1" precision="1"/>
<s:ArrayCollection id="benchmarks"/>
<mx:MultiTopicConsumer id="consumer" destination="f" message="messageHandler(event)"
fault="log(event.faultString)"/>
</fx:Declarations>
<s:layout>
<s:HorizontalLayout gap="0"/>
</s:layout>
<s:SkinnableContainer width="300" height="100%" styleName="sidebar">
<s:layout>
<s:VerticalLayout paddingTop="8" paddingLeft="8" paddingRight="8" paddingBottom="8"/>
</s:layout>
<s:Panel title="Server Feed" width="100%" skinClass="flex.skeletons.traderdesktop.skins.SidebarPanelSkin">
<s:layout>
<s:VerticalLayout paddingBottom="4" paddingRight="4" paddingLeft="4"/>
</s:layout>
<mx:Form paddingBottom="4" paddingLeft="4">
<mx:FormItem label="Number of Symbols" width="100%">
<s:NumericStepper id="nsNumStocks" minimum="1" maximum="1000" width="60" stepSize="1" value="100"/>
</mx:FormItem>
<mx:FormItem label="Number of Threads">
<s:NumericStepper id="nsNumThreads" minimum="1" maximum="20" width="60" stepSize="1" value="1"/>
</mx:FormItem>
<mx:FormItem label="Sleep Time (ms)">
<s:NumericStepper id="nsSleepMillis" value="10" stepSize="1" width="60" minimum="1" maximum="1000"/>
</mx:FormItem>
<mx:FormItem label="Server Throughput" paddingTop="8">
<s:Label enabled="false" text="{nf1.format(1000 / nsSleepMillis.value * nsNumThreads.value)} mps"/>
</mx:FormItem>
<mx:FormItem label="Frequency">
<s:Label enabled="false" text="{nf1.format(1000 / nsSleepMillis.value * nsNumThreads.value / nsNumStocks.value)} mps/symbol"/>
</mx:FormItem>
</mx:Form>
<s:controlBarContent>
<s:Button label="Start Feed" click="startFeed()"/>
<s:Button label="Stop Feed" click="stopFeed()"/>
</s:controlBarContent>
</s:Panel>
<s:Panel title="Client Subscription" width="100%" skinClass="flex.skeletons.traderdesktop.skins.SidebarPanelSkin">
<mx:Form>
<mx:FormItem label="Channel">
<s:ComboBox id="comboChannels" labelField="id" enabled="{!consumer.subscribed}"/>
</mx:FormItem>
<mx:FormItem label="Max Frequency">
<s:NumericStepper id="nsMaxClientFrequency" minimum="0" maximum="5000" value="0" stepSize="1" width="50"/>
</mx:FormItem>
<mx:FormItem label="Frame Rate">
<s:NumericStepper id="nsFrameRate" minimum="1" maximum="300" value="24" stepSize="1" width="50"/>
</mx:FormItem>
<mx:FormItem label="Rendering" paddingTop="8">
<s:CheckBox id="cbRender" click="dg.dataProvider = cbRender.selected ? stockList : null" selected="true"/>
</mx:FormItem>
<mx:FormItem label="Current Latency">
<s:Label id="latencyLbl" text="n/a"/>
</mx:FormItem>
</mx:Form>
<s:controlBarContent>
<s:Button label="Subscribe" click="subscribe()" enabled="{!consumer.subscribed &amp;&amp; stockList != null}"/>
<s:Button label="Unsubscribe" click="unsubscribe()" enabled="{consumer.subscribed}"/>
</s:controlBarContent>
</s:Panel>
<s:Panel title="Log" width="100%" height="100%" skinClass="flex.skeletons.traderdesktop.skins.SidebarPanelSkin">
<s:TextArea id="logArea" width="100%" height="100%" borderVisible="false"
updateComplete="logArea.scroller.verticalScrollBar.value = logArea.scroller.verticalScrollBar.maximum"/>
</s:Panel>
</s:SkinnableContainer>
<s:SkinnableContainer width="100%" height="100%" styleName="body">
<s:layout>
<s:VerticalLayout paddingTop="8" paddingLeft="8" paddingRight="8" paddingBottom="8"/>
</s:layout>
<controls:FastDataGrid id="dg" width="100%" height="70%" borderVisible="true">
<controls:columns>
<mx:DataGridColumn headerText="Symbol" dataField="symbol"/>
<mx:DataGridColumn headerText="Open" dataField="open" textAlign="right"/>
<mx:DataGridColumn headerText="Last" dataField="last" itemRenderer="flex.skeletons.traderdesktop.renderers.LastRenderer" textAlign="right"/>
<mx:DataGridColumn headerText="Change" dataField="change" itemRenderer="flex.skeletons.traderdesktop.renderers.ChangeRenderer" textAlign="right"/>
<mx:DataGridColumn headerText="High" dataField="high" itemRenderer="flex.skeletons.traderdesktop.renderers.HighRenderer" textAlign="right"/>
<mx:DataGridColumn headerText="Low" dataField="low" itemRenderer="flex.skeletons.traderdesktop.renderers.LowRenderer" textAlign="right"/>
</controls:columns>
</controls:FastDataGrid>
<s:Panel title="Performance" width="100%" height="30%">
<mx:DataGrid id="dgPerf" dataProvider="{benchmarks}" width="100%" height="100%" borderVisible="false">
<mx:columns>
<mx:DataGridColumn headerText="Symbols" dataField="stocks" textAlign="center" headerWordWrap="true"/>
<mx:DataGridColumn headerText="Threads" dataField="threads" textAlign="center" headerWordWrap="true"/>
<mx:DataGridColumn headerText="Sleep (ms)" dataField="sleep" textAlign="center" headerWordWrap="true"/>
<mx:DataGridColumn headerText="Target Server mps" dataField="throughput" textAlign="center" headerWordWrap="true"/>
<mx:DataGridColumn headerText="Channel" dataField="channel" textAlign="center"/>
<mx:DataGridColumn headerText="Max Freq." dataField="maxFrequency" headerWordWrap="true" textAlign="center"/>
<mx:DataGridColumn headerText="Frame Rate" dataField="frameRate" headerWordWrap="true" textAlign="center"/>
<mx:DataGridColumn headerText="Render" dataField="rendering" textAlign="center"/>
<mx:DataGridColumn headerText="Total msgs" dataField="messageCount" textAlign="center" headerWordWrap="true"/>
<mx:DataGridColumn headerText="Actual Client mps" dataField="mps" textAlign="center" headerWordWrap="true"/>
<mx:DataGridColumn headerText="Avg Latency (ms)" dataField="latency" textAlign="center" headerWordWrap="true"/>
</mx:columns>
</mx:DataGrid>
<s:controlBarContent>
<s:Button label="Run Benchmark" click="startBenchmark()" enabled="{consumer.subscribed}"/>
</s:controlBarContent>
</s:Panel>
</s:SkinnableContainer>
</s:Group>