Tuesday, July 21, 2009

Drag and Drop: double binding?

We had a minor fracas at work this morning. The burning issue: does Flex 3, in fact, have double binding?

The official answer is no, Flex 3 does not have double binding. But... it seems that in certain cases, doubly-bound behavior is nevertheless present.

For example:

* create a tree, and enable drag and drop behavior
* set your tree's data provider as an ArrayCollection with nested Arrays; in other words, a treelike structure.
* grab a handy data serialization package, such as as3yaml (http://code.google.com/p/as3yaml/), to display the data structure, then run in debug mode.

Then start dragging elements around, and you'll see something natural: as elements change position, the underlying data structure changes. In other words, the data provider is the model, and the Tree is the view... but also the controller. Change the tree, change the underlying data provider.

Acts like double binding. In reality, it's boilerplate code underwiring the drag and drop attributes on things like Tree and DataGrid. Remember: [Bindable] is boilerplate code forcing the view to change when the model changes. And this one sort of seems like the reverse... hence it is very like double binding.


My sample code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="handleCreationComplete()">

<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import org.as3yaml.*;

private var node1:Object =
{
label: 'parent 1',
index: 0,
children:
[
{ label: 'A' }, // , index: 0 },
{ label: 'B' }, // index: 1 },
{ label: 'C' }, // index: 2 }
]
};

private var node2:Object =
{
label: 'parent 2',
index: 1,
children:
[
{ label: 'D' }, //index: 0 },
{ label: 'E' }, //index: 1 },
{ label: 'F' }, //index: 2 }
]
};

private var node3:Object =
{
label: 'parent 3',
index: 1,
children:
[
{ label: 'G' }, //index: 0 },
{ label: 'H' }, //index: 1 },
{ label: 'I' }, //index: 2 }
]
};


[Bindable]
private var mydata:ArrayCollection = new ArrayCollection([node1, node2, node3]);

private function handleCreationComplete():void
{
dump();
}

private function dump():void
{
var s:String = YAML.encode( mydata.source );
trace( s );
}
]]>
</mx:Script>
<mx:Tree id="mytree"
dataProvider="{mydata}"
dragEnabled="true"
dropEnabled="true"
dragComplete="dump()"
editable="true"
x="40" y="40" width="300" height="300"
/>
</mx:Application>

Monday, July 20, 2009

Refresh that ArrayCollection

So I have this ArrayCollection, which looks basically like this:

[Bindable]
public var d1:ArrayCollection = new ArrayCollection(
[
{ label: 'first', code: 'A', num: 4 },
{ label: 'sec', code: 'B', num: 7 },
{ label: 'tria', code: 'C', num: 9 }
]
);

It's the dataProvider for a DataGrid, like this:

<mx:DataGrid id="dg1"
dataProvider="{d1}"
dragEnabled="true"
dragDrop="handleDragDrop(event)"
dropEnabled="true"
x="320" y="126">
<mx:columns>
<mx:DataGridColumn headerText="Column 1" dataField="label"/>
<mx:DataGridColumn headerText="Column 2" dataField="code"/>
<mx:DataGridColumn headerText="Column 3" dataField="num"/>
</mx:columns>
</mx:DataGrid>

And then, I've got a horizontal slider that sets a value in the ArrayCollection, like this:

<mx:HSlider id="hslider" x="10" y="320" width="302"
minimum="1" maximum="10" snapInterval="1"
change="d1.getItemAt(0).num = this.hslider.value"
labels="{[1,2,3,4,5,6,7,8,9,10]}" />

So I can slide the slider, and the "num" attribute of the first element in d1 changes, but the display list in the datagrid doesn't change. And so I learned (or perhaps re-learned) a lesson today:

Whenever you change data in a data provider, also call refresh() on it.

Not only does this re-run filters and sorts, it also refreshes the display. So when a single value changes, calling refresh() will update display lists that are bound to the array.

So, the slider's change property should look like this:

change="d1.getItemAt(0).num = this.hslider.value; d1.refresh()"

I'm actually using these widgets in a web application I'm slowly working on, which has a web of components that interact with each other. It's wargame rules for starship combat for a pencil-and-paper game called Traveller:

http://eaglestone.pocketempires.com/rules/t5/starships/Shipyard.html

Monday, July 6, 2009

Data Binding is Beautiful

One of THE best things about Flex is its data binding. Back in Java days, you had to write synchronization code for every GUI component backed by a data model. Thanks to Flex, all that boilerplate boredom is GONE. As a result, I can populate data grids with ease. For example, check out this Flex app I built in a few hours yesterday:

http://eaglestone.pocketempires.com/rules/t5/starships/TechChooser.html

Monday, May 11, 2009

Comprehensive Ruby Archive Network

As I was posting on my Flex blog, I started to wonder if there's a design assumption going on that I hadn't considered.

I was thinking that Ruby could use a repository where individuals can share modules. Basically, a large collection of software and documentation.

Or do we?

Take Perl. Do you want to turn your Perl script into a CGI system? Go to CPAN -- the Comprehensive Perl Archive Network -- and grab the CGI module (okay, bad example, it's included in the standard distribution, but you get the idea). CPAN has bazillions of modules, and is an invaluable resource for handling whatever you need to handle. Proofs of concepts get hammered out, new ideas get born, and thoughtful robust code is freely available. All in one practical place.

But, perhaps Ruby has a higher level of focus. I see RubyForge: support for "projects", i.e. full applications, hinting that the modules there require just a bit too much cohesion to be generally useful. Instead of plug-in modules that you can use to add functionality to an application, does Ruby tend to focus on the applications (or projects) themselves?

Comprehensive Flex Archive Network

We need a repository where individuals can share ActionScript and Flex modules. Basically, a large collection of Flex software and documentation.

Or do we?

Take Perl. Do you want to turn your Perl script into a CGI system? Go to CPAN -- the Comprehensive Perl Archive Network -- and grab the CGI module (okay, bad example, it's included in the standard distribution, but you get the idea). CPAN has bazillions of modules, and is an invaluable resource for handling whatever you need to handle. Proofs of concepts get hammered out, new ideas get born, and thoughtful robust code is freely available. All in one practical place.

But, perhaps Flex has a higher level of focus. I've noticed that most of the modules we create require just a bit too much cohesion to be generally useful. Instead of plug-in modules that you can use to add functionality to an application, Flex tends to focus on the applications (or projects) themselves. So it's a higher-level view.

Thursday, May 7, 2009

Password Strength Meter

http://eaglestone.pocketempires.com/flex/passwordMeter/PasswordMeter.html

I found a good starting example of a password strength meter at http://subeesh.wordpress.com/2008/02/19/flex-password-strength-meter/#comment-69

It's got a nice graphical component, and great regular expression checkers, but the algorithms used to evaluate password strength are unknown to me. I imported his gradient bar into my component, which is simply a VBox which contains the graphics context for the gradient and algorithm. The component also requires a "dataProvider", which is just the TextInput component you wish to evaluate.

The source is here:

http://eaglestone.pocketempires.com/flex/passwordMeter/Password.mxml

Monday, May 4, 2009

ComboBox Mediator

Friday I applied the Mediator pattern to a pair of ComboBoxes.

The Mediator, in short, is an interaction between two objects that's been encapsulated into a third object. A go-between. It's almost like a Decorator that works on two objects instead of one.

We have Combo Boxes to choose your country and state. So when a user picks a country, then the list of states has to be filtered for valid selections. We do this all throughout our code for various custom forms (the forms themselves all vary in significant ways, otherwise we could simply re-use one form).

Instead of cloning our code over into yet another class, I wrote a ComboBoxMediator, which wires two ComboBoxes together in a source-sink relationship. The MXML looks like this:


<bgs:ComboBoxMediator
sourceBox="{countryCodeComboBox}"
sourceSelectedValue="{address.countryCode}"
targetBox="{provinceCodeComboBox}"
targetSelectedValue="{address.provinceCode}"
/>


The class itself is short. It sets up filtering in the province ComboBox and adds a change event listener on the country ComboBox.

One important step that I originally didn't anticipate is when the province box is being refreshed: I have to check to see if the filtered data results in no data at all (in other words, in cases where a chosen country doesn't have province data yet assigned to it). In this case, I set the data provider on the province box to an inline array with only one value: "Not Applicable".

Tuesday, April 28, 2009

Quivira - Planning 2

This afternoon I'm using a hex editor to analyze the old Commodore 64 "Map Disk" for 7 Cities of Gold, and here's the physical data, starting from file position 00000000:

"Empty" cells are initially marked with an 01 decimal.

Every 0x100 bytes there's a decimal 10 marker.

Starting in cell $01500, and every 0x100 bytes after, there's an 0x4b.

Finally, we get to the disk header block, starting at 0x16500 with:

(hex) 12 01 41 00,
(hex) 15 ff ff 1f, repeated 17 times,
(hex) 11 fc ff 07,
(hex) 13 ff ff 07, repeated 6 times,
(hex) 12 ff ff 03, repeated 6 times,
(hex) 11 ff ff 01, repeated 5 times.

After that comes the disk's title label, in this case, simply "MAP" in PETSCII:

$16590: 4d 41 50, followed by 0xa0 15 times.

Then there's the disk's two identifiers, separated by an a0:

$165a2: 45 41 a0 32 41, then four more a0's.

Then it's zeros until $16601, which is a big fat FF, then another pile of zeros.

Starting at $16700, we resume the 4b every 0x100 bytes, with 01s in between.

It looks like the actual data begins at $17300, with what seems to be blocks of data 0x100 bytes long. In some of the data blocks, there's a two byte header, then another two byte header at 0x080 bytes (I think).

$17300 to $173ff is very sparse.
$17400 to $174ff is a bit less so.
$17500 to $17599 has even more data.
$17600 to $176ff has more.
$17700 to $177ff is quite busy.

$17800 to $178ff is also busy, but has an unusual pattern not found with the others. Starting at 0x66, descending to 0x10, then climbing up to the 0x90s and 0xa0s at the end of the block. Almost a random walk, but with a deterministic bent, whereas the others are relatively random. Climate data? Climate and altitude?

Then $17900 to $179ff, every value except 5 are between 0xf0 and 0xf8. The exceptions are 0x0a, 0x03, 0x03, and 0x0a, scattered among the 0xf- data. These look like "flags" tied to areas of the map, or something.

$17a00 to $17aff are nearly all 0x3a through 0x3f, with 3 exceptions (0xff, 0x00, and 0x00). Interestingly, the two 0x00's correspond exactly to two of the relative positions in the previous data block. I suspect these data may represent native villages, whether they've been visited, whether there's a mission or fort there, etc. I am probably wrong.

$17b00 to $17bff is random-looking data again.

$17c00 to $17cff is somewhat random, with a lot of zeros surrounding strings of numbers that appear related.

$17d00 to $17dff is mostly zeros, with occasional random-appearing numbers. $17dc0 to $17dff are all zeros. I suspect this is not a single block of data.

$17e00 to $17eff is similar to the previous block, except toward the end: from $17e90 to $17ebf there are nothing but zeros, and from $17ec0 to $17eff it's 0xff's.

$17f00 goes back to our "no data" format: 0x4b every 0x100 bytes, with 0x01 filler.


And then we get some funny data starting at $1a400 and lasting until $1a7ff. Lots of repeating values, and maybe some headers, but I'm not sure.

Then from $1a800 to $1bbff we're back to "no data".

Then there's some data structures from $1bc00 to $1bdff.

Then a block of data from $1be00 to $1beff.

Then, something interesting -- a repeated pattern from $1bf00 to $1bfff: a block that appears to be identical to $17800 to $178ff. A copy of the map? What part of the map? There will have to be a copy when something is changed on the player map, so you can reset and start over. Maybe this is native village data, but then why would the numbers decrease, and then increase again?

$1c000 is regular, random-walk-like numbers, starting at 03 and creeping down to 00, then bouncing back up to 04 halfway through... hey wait, that halfway point has a two-byte marker. Many of the ones that I think are "map" related must have this.

$1c100 starts out with zeros, then a semi-repeating pattern of ff ff's interspersed with zeros, with 7s and 8s as boundaries... in other words, multibyte bitstrings of all 1s followed by all 0s, most of the time. This peters out by $1c1a0, turning into mostly zeros with an occasional number.

From $1c1d0 to $1c4ff there's just zeros, except for a 0x01 and an 0x11 at the tail end of the block.

$1c400 to $1c46f are zeros, then there's a string of 0x11s and an 0x21, then from $1c480 on it's zeros again.


But finally I think I can see the actual map -- the terrain. It begins around $1ca40 (probably the ocean is zeros, which could start at any point before here) and billows along until around $1ceff. Then again from $1cf00 (starting with a short header and a pile of zeros) with actual land appearing around $1d200, and again (I think) at $1d700
to $1dfff, again from $1e000 to $1e7ff (?), $1e800 to $1efff, $1f000 to $1f7ff, $1f800 to $1ffff, $20000 to $207ff, $20800 to $20fff, $21000 to $217ff, $21800 to $21fff... I think.

The data consists of 0x01, 0x11, 0x12, 0x1c, 0x2b, 0xb1, 0xbc, 0xcb, 0xbd, 0xdc, 0xcd, 0xbb, 0xcc, 0xdd, 0xdb. I believe these bit patterns correspond to a particular set of tiles.

The blocks here appear to be in 0x80 byte blocks. More interestingly, every 8 bytes appears to represent one layer of a block of map data that's probably 0x80 bytes in size.

The map ends at $289ff. After that, it's just 0x4b every 0x100 bytes, padded in between with 01s.

Analysis comes later, or maybe never.

Wednesday, April 22, 2009

def() and a module idea

Since I don't use Ruby for my job, this blog is in danger of dying a horrible but quick death.

I like def

...however, there is one thing I'd like to put on the record about Ruby. I like "def" to define methods, functions, subroutines, whatever you want to call them. Def has more currency than Perl's "sub". If Perl6 allowed "def" as a synonym for "sub" I'd be pleased.

Sound like a meaningless detail? The devil, I say, is in the details.

Something I don't think I like, on the other hand, is using "end" to close definitions (class...end. def...end.). True, it's obvious, and it's only one more keystroke than [shift+close bracket]. But, I seem to prefer keywords having more utility than as a block delimiter.

But that IS a reasonably trivial detail, one I can overlook.


A module idea

There's a science-fiction game called Traveller that's been in print for thirty years, and one of the key elements in the game is a world abstract, called the Universal World Profile, or UWP for short. It is self-contained with a handful of data elements, some fully derived data, some externally imposed data, and some computations you can perform. In short, perfect for a module. I think a good learning exercise is to port my Perl UWP module into Ruby.

Spacewars - planning

Today, I'll shift to musing about Spacewars. Back when I did this in Java, a co-worker and I hit on a Gravity Model to manage Newtonian variations in the game. Plug in your custom gravity model based on your preferred settings and all that.


Design Thought

I'm taking design a step further and implementing a Chain of Command pattern on top of this pluggability. There are discrete steps in these sorts of games, and all of them operate directly on the units in the playing field -- essentially a Blackboard model, except in order to prevent chaos I'll sequence the blackboard elements into a Chain of Command. So the game loop is managed by GameStep objects, which operate independently of each other:

- Gravity Model
- Input Model
- Position Tracker
- Impact Model

The interface may simply be:

public function accept(state:GameState):void {...}
public function name():String {...} // for game settings chooser...


The control loop therefore becomes very lightweight.

I could implement these as Template Methods, but I like the "pluggable" modularity of Chain of Command. It's one of the side-effects of Object-Oriented programming that I enjoy (OO tends to still be a bit overrated at times).

It's also easily extensible: if my game becomes cool and I want to play my friend Dan Terrill over the internet, I can later code up a NetworkInputModel that lets us play. If I want to create a Lunar Lander variant, I code up a SurfaceGravityModel that exerts a "downward" pull on all objects in the game, and a variant ImpactModel that detects a safe vs. unsafe landing. Plus it could retain the combat elements... combat lunar lander... cool.


Implementation Thought

One of the few things I know about Flex is that the Canvas (and Panel) objects are positionable graphics objects which "own" things which are displayed. That's all fine and well, but I found a side effect that I hope I can exploit: point rotations of objects on a Canvas are rotated relative to the origin of the Canvas.

Here's why that's important: If I create a separate Canvas for each object in play, centered on its origin, then I can move and rotate the Canvas very easily.

Normally, when you rotate an object, you translate its points relative to the origin, then apply the transformation (rotation), then re-translate back to their actual position, then move the object to that new position. It's lots of work, and also a solved problem... for decades. If I can simply set the rotation angle of an owning Canvas, however, all that work is done for me. Simpler application code. Less boring work. Win-win.

Tuesday, April 21, 2009

Quivira - planning

First some data structure musings.

In Seven Cities of Gold (the original game by EA), the map was an array of bytes, where the bit patterns represented terrain, flora, and settlements.

My adaptation is still very data-light, but it's not that light. Instead of using bitfields to represent the Map, I'm using a Set of maps, where each map is an Array of Strings. Thus I can store the maps in text format, and can inspect and edit them with a text editor...

Monday, April 20, 2009

Retro coding

Last week I had an attack of nostalgia for retro gaming. If you're Generation X, you may remember the 8-bit games that came out between 1982 and 1986 or so, especially from Electronic Arts. And you'll also remember when Space Invaders, Asteroids, and similar came out.

I have two abiding games of interest: Seven Cities of Gold by Electronic Arts (http://en.wikipedia.org/wiki/The_Seven_Cities_of_Gold_(video_game)), and Spacewars (http://en.wikipedia.org/wiki/Space_Wars).

I tried to build Spacewars in Java ten years ago. Now I want to write it for Flex, which I think will be an easier task by the way. Ships, projectiles, and the gravity source are the interacting objects in the game. I use the term "gravity source" because I believe that the Spacewars codebase is nearly identical to that of the classic "lunar lander" game.

I've tried to design and write "retro"-styled games that use ideas from Seven Cities of Gold for a long time. Once again, Flex has rekindled my imagination, and I'd like to create an interactive simulation, with retro graphics reminiscent of the original Seven Cities game. The main feature of this game is the Map.

In both cases, the Sprite class will feature prominently.

Monday, April 6, 2009

Metric Clock

For those who like the bizarre for the sake of the bizarre, a clock which divides the day into 100 hours of 100 'moments' (each is about 9 seconds long).

Check it out here: http://eaglestone.pocketempires.com/bin/metrictime/MetricClock.html

I've added some spaces in the MXML to make it displayable...

< ?xml version="1.0" encoding="utf-8"?>
< mx:Application 
 xmlns:mx="http://www.adobe.com/2006/mxml" 
 layout="absolute"
 creationComplete="creationCompleteHandler()"
 >
 < mx:Script>
  < ![CDATA[
           import flash.display.Sprite;
           import flash.utils.Timer;

           private var clock:Sprite = new Sprite();
           private var hour:Sprite = new Sprite();
           private var sec:Sprite = new Sprite();

           public function creationCompleteHandler():void
           {
             clock.graphics.lineStyle(2);
              clock.graphics.drawCircle(0,0,100);
              
              // draw tick marks on clock face
              var outerRadius:int = 99;
              
              for ( var m:int=0; m<100 data-blogger-escaped-br="" data-blogger-escaped-m="">              {
                var innerRadius:int = 96;
                clock.graphics.lineStyle(1, 0x000000, 100);
                
                if ( m % 10 == 5 )
                {
                 innerRadius = 94;
                    clock.graphics.lineStyle(2, 0x000000, 100);
                 }
                 
                var theta:Number = m * 3.6 * Math.PI / 180;
                
                 clock.graphics.moveTo(Math.cos( theta ) * outerRadius, 
                                       Math.sin( theta ) * outerRadius);
                 clock.graphics.lineTo(Math.cos( theta ) * innerRadius, 
                                       Math.sin( theta ) * innerRadius);
              }
              
              myCanvas.move(150,150);
              myCanvas.rawChildren.addChild(clock);
              myCanvas.rawChildren.addChild(hour);
              myCanvas.rawChildren.addChild(sec);

              // draw 'hour' hand
              hour.graphics.lineStyle(2, 0x000000, 100);
              hour.graphics.moveTo(0, 0);
              hour.graphics.lineTo(0, -60);

              // draw 'second' hand
              sec.graphics.lineStyle(0, 0x0ff00f, 100);
              sec.graphics.moveTo(0, 0);
              sec.graphics.lineTo(0, -90);
 
              var timer:Timer = new Timer(400);
              timer.addEventListener(TimerEvent.TIMER, tick);
              timer.start();
           }

           public function tick(evt:TimerEvent):void
           {
              var now:Date    = new Date();
              var time:Number = now.hours * 3600 + now.minutes * 60 + now.seconds + now.milliseconds / 1000;
              var metric:int  = time * 100/864;

              var hrs:Number  = int(metric / 100);
              var secs:Number = metric % 100;

              hour.rotation = 3.6 * hrs;
              sec.rotation  = 3.6 * secs;

              when.text = "";
              if ( hrs  < 10 ) when.text = "0";
              when.text += hrs + ":";
              if ( secs < 10 ) when.text += "0";
              when.text += int(secs);
           }
  ]]>
 < /mx:Script>
 < mx:Canvas id="myCanvas" x="0" y="0" width="200" height="200">
 < /mx:Canvas>  
 < mx:Text id="when" x="131" y="264" text="" width="52"/>
< /mx:Application>

Monday, March 30, 2009

Shared Object gotchas

If you don't understand how something works, bugs will ensue.

Experience is what you get when you don't get what you want.

I've got a zillion of these sayings.

Anyway.

The SharedObject is a Flash-cookie way to cache data locally. If one's not there when you reference it, it's created, automagically, in memory until you flush it to disk.

Here's the fun part. We have persisted data that goes stale regularly, and we want to test our caches to make sure they're fresh. We have a GUID for general tests, but we also just test to see if the cache "exists"... and that's a problem. How do you know a cache "exists" if it's created the first time you look at it?

Our fix is to check the object's size. If its size is 0 (zero), then it was just created and doesn't in fact "exist". Therefore we know to go to the database to fetch our data.

Tuesday, March 24, 2009

TFTP Packet Factory

Straightforward, but fun.


class PacketFactory # TFTP = RFC 1350, 2347, 2348 (not needed here)
# #
# 2 bytes string 1b string 1b string 1b string 1b ... #
# +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+--> #
# | 01 |filename| 0 | mode | 0 | opt1 | 0 | value1 | 0 | < #
# +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+--> #
# string 1b string 1b #
# >-------+---+---~~---+---+ #
# < optN | 0 | valueN | 0 | #
# >-------+---+---~~---+---+ #
# #
# RRQ packet #
# #
# The mode field contains the string "netascii", "octet", or "mail" #
# #
def buildRRQ( filename, mode, *opts )
packet = "\0\1" + filename + "\0" + mode + "\0"
opts.each { |o| packet += o + "\0" }
return packet
end

# #
# 2 bytes string 1b string 1b string 1b string 1b ... #
# +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+--> #
# | 02 |filename| 0 | mode | 0 | opt1 | 0 | value1 | 0 | < #
# +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+--> #
# string 1b string 1b #
# >-------+---+---~~---+---+ #
# < optN | 0 | valueN | 0 | #
# >-------+---+---~~---+---+ #
# #
# WRQ packet #
# #
def buildWRQ( filename, mode, blocksize, octets )
packet = "\0\2" + filename + "\0"
+ mode + "\0"
+ blocksize + "\0"
+ octets + "\0"
return packet
end
# #
# 2 bytes 2 bytes n bytes #
# ---------------------------------- #
# | 03 | Block # | Data | #
# ---------------------------------- #
# #
# DATA packet #
# #
def buildDAT( blocknum, data )
return "\0\3" + [blocknum, data].flatten.pack("n*")
end
# #
# 2 bytes 2 bytes #
# --------------------- #
# | 04 | Block # | #
# --------------------- #
# #
# ACK packet #
# #
def buildACK( blocknum ) return "\0\4" + [blocknum].pack("n") end

def buildACKfromDAT( packet )
packet = packet[0..3]
packet[1] = "\04"
return packet
end
# #
# 2 bytes string 1b string 1b...string 1b string 1b #
# +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ #
# | 06 | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | #
# +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ #
# #
# OACK packet [RFC 2347] #
# #
def buildOACK( opt_hash )
packet = "\0\6"
opt_hash.each_pair { |k,v| packet += k + "\0" + v + "\0" }
return packet
end

# #
# 2 bytes 2 bytes string 1 byte #
# ----------------------------------------- #
# | 05 | ErrorCode | ErrMsg | 0 | #
# ----------------------------------------- #
# #
# ERROR packet #
# #
# Error Codes #
# #
# Value Meaning #
# #
# 0 Not defined, see error message (if any). #
# 1 File not found. #
# 2 Access violation. #
# 3 Disk full or allocation exceeded. #
# 4 Illegal TFTP operation. #
# 5 Unknown transfer ID. #
# 6 File already exists. #
# 7 No such user. #
# 8 Invalid option value requested. [RFC 2347] #
# #
def buildERR( errcode, errmsg )
packet = "\0\5" + [errcode, errmsg].pack("na*x")
return packet
end
end

Hiding components functionally

We came up with a neat idea for hiding UI Components. We can apply a Flex version of the Apache Java commons "Predicate" interface, like so:


package blah.blah.blah
{
interface IPredicate
{
function evaluate(): Boolean;
}
}


Then we can pass an implementing object to a subclass of UIComponent, which will trigger its visibility based on the evaluation of the object:


package blah.blah.blah
{
class UIVanishingComponent extends UIComponent
{
var predicateObject:IPredicate;

UIVanishingComponent( po:IPredicate )
{
super();
this.predicateObject = po;
}


.... but now what? The "visible" boolean triggers the dispatch of a SHOW event, which appears to simply alert the listener that the component is now visible... in other words, someone else controls the rendering of this object? So we either create a handler that listens for the Predicate to change its state, or we have to figure out how to intercept or redirect the renderer for this component... or do something else.

Maybe the answer is in using Flex's event model. Anyone know? More to come.

Tuesday, March 17, 2009

Try/Catch with Shared Objects

I've done a bazillion Try/Catches in Java, so in a way this is not a big deal... but I'm a Flex tyro, so in another way this is a big deal.

We store a lot of data locally, as Shared Objects. This reduces the burden on the server and SQL Server. For the first time, though, some data schema changed, which means locally cached data now fails to load, and therefore throws an error. This was easily fixed, with try/catch.


// let's not assume the data is in good condition...
// if it's corrupted or the schema's been changed
// then we throw an error.
try {
cache[cacheName]
= SharedObject.getLocal(cacheName);
}
catch( err:Error ) {
... flag this data to be re-fetched ...
}


Problem solved.

UDP.rb

First step for my TFTP app is to build a simple UDP class.

UDP is a connectionless datagram protocol. You can bundle up some data, send it off to a target machine, and forget about the whole thing. It might not even make it to the destination; nobody cares. This has led the masses to assume that UDP is Unreliable. Consider, however, that UNIX uses UDP for internal messaging protocols. Also consider that, with just a small amount of effort, one can make lock-step communications with ACKing. This is how most networked games push their data around -- TCP would be unacceptable; it's a resource hog. And I always prefer to conserve my resources.


require "socket"

class UDP

def send( msg, host, port, flags=0 )
sock = UDPSocket.new
sock.send( msg, flags, host, port )
sock.close
end

def recv( port, maxblocksize )
sock = UDPSocket.new
sock.bind( "localhost", port )
msg = sock.recv( maxblocksize )
p sock.addr
sock.close
return msg
end
end


Two methods: send() and recv(). How much simpler can you get? I think the lack of a "connection" is a strength of UDP: the thing is just bare bones. I love that.

Here's some quick-and-dirty test code:


client = UDP.new
client.send( msg, server_addr, server_port )

server = UDP.new

loop do
msg = server.recv( server_port, maxbytes )
p msg
exit if msg.chomp.eql?( 'exit' )
sleep 1
end

Monday, March 16, 2009

Old Protocols for a New Web

When I say "New Web" I mean a hypothetical, non-existent internet that uses existing technology in novel ways to improve efficiency and presentation. And Ruby's a great language for prototyping this.


My first project with Ruby is an implementation of TFTP. TFTP is a poor man's FTP. It has three RFC's, all short and sweet. Here are the things that makes it nice:

(1) simple. It's a small protocol, and so anyone can implement it. It's so small and so useful that it's used to boot up routers.

(2) proven. Again, it's one of the first applications ported to a new server. It's how servers get upgraded. That should tell you something about its utility.

(3) relatively non-hackable. Again, due to its simplicity. Especially if it's read-only, i.e. doesn't support writing files.

(4) fast. Because it's built on UDP, there's very low overhead, and no expensive handshaking, TCP nailup costs, etc.

(5) svelte. Again, because it uses UDP, there's no TCP nailup cost, and other associated expensive processing.

(6) synchronous. Because UDP has a willy-nilly transmission style, TFTP synchronizes transfer with numbered packets and does its own ACKs, retransmits, etc. If you thought UDP was unreliable, well, there's ways around that, and those ways are easy.

I'm still working on the implementation, but preliminary results are encouraging. The only discouraging thing is that servers generally lock down UDP transmissions as a rule. That's just something I'm going to have to face: it's not secure as-is, what with the ability to write files at will into the target file system.

BUT, what I want to do is implement browser functionality using TFTP as the transport mechanism. This means it's a read-only protocol... or, more correctly, "writes" are translated as "posts" rather than file transfers. So, no writing files directly into the file system. This puts security on par with current browsers.

Of course, on top of TFTP would have to be a new kind of browser, but that's a nontrivial task that I'll leave for the future. Perhaps it can just be a plugin for Firefox. And then, there's the question of what the presentation layer is going to be. CSS and HTML? Sure, but the scientific community is not served by these hacked styles. I want a TeX display layer. Once again, that's another nontrivial task that I'll have to leave for the future. I'll be happy just to get TFTP working.

The Button and the Event

So there's a Button that triggers a complex event. In short, it calls a remote object thru Granite to JBoss, and does some neato stuff.

Problem: users have (accidentally) double-clicked (and even triple-clicked) that Button, resulting in multiple event dispatches, bad data, and some server-side exceptions.

Normally, I'd disable the Button.

 button.enabled = false; 

Then, when processing is done, I'll re-enable it. Easy, right?

 button.enabled = true;

But, in this case, that's not fast enough -- the user can click that button five or six times before the Button's disabled! (I didn't think that was possible, but it is).

OK then, I'll cut it off at the source: first, I removed the ActionScript inside the "click" attribute of the MX element:


< id="bntCreateMan">


...and I put the code into an explicit handler, like this:


// this method is triggered ONLY by clicking
// the "Create Manifest" button
private function
handleCreateNewManifest(event:MouseEvent):void
{
this.btnCreateMan.removeEventListener(
MouseEvent.CLICK,handleCreateNewManifest);
...
createNewManifest();
}


Add that handler in the creationComplete() method, and voila!

Note the sneaky call in the handler: as soon as we click the Button, the handler removes itself. Thus, subsequent button clicks fall on deaf ears.

Now to put the handler back. We want it back when the manifest data is handled... we have a handler for that of course. So we have:


private function
handleManifestData(event:ResultEvent):void
{
(...some code...)
this.btnCreateMan.addEventListener(
MouseEvent.CLICK,handleCreateNewManifest);
}


How clever of us.

New Problem: That handler, shown above, is invoked by other processes as well. So what really happens is that the event handler gets attached to that Button multiple times, and only removed when the Button is pressed. Aiigh!

The solution is mundane and so old school: instead of manipulating the event listener, we add a Boolean value that the Button's listener uses. If it's false, we go ahead and kick off a manifest creation. Then, back in the ManifestData handler, if the boolean is true then we re-set it.

It's not pretty, it feels a bit kludgy, but it seems to work best.