Thursday, November 22, 2012

Passing additional information between nodes #AlphaLabsCC

As promised, here's an example of how to pass additional information between nodes/devices as part of your AlphaLabs projects.

I'm going to show how to pass the color of the current node to other devices, along with it's position, so that instead of just having your own node coloured and all other nodes white, every node is represented by the theme colour of the device it is actually on.
I'm also going to show how to do this with the Silverlight version as I'm assuming that everyone will be able to follow along with that without issue.
Something like this:

Obviously this makes it harder to tell which is your own node (hint, in the base code it's slightly larger than the others) but I'm sure you can cope and come up with interesting and exciting ways of indicating this in your apps/experiments/art.

So how do we do this?

It's all down to the power of the Tag!
I'm sure you're familiar with the Tag property of a Control or FrameworkElement. It's a useful miscellaneous property for storing arbitrary information in an object. Well, NodeGardenLib.Node has one too. The only difference is that in the Node it's a string, not an object.

We can add anything (within reason) to this property and it can be passed between devices via the underlying messaging functionality.

We'll start by setting the Tag when we create the node.
In MainPage.xaml.cs:
        private void CreateOwnNode()
        {
            var myNode = new MyVisualNode(this.rand, MainCanvas, this.gardener);

            this.gardener.AddSelfNode(myNode.X, myNode.Y);

            myNode.Id = this.gardener.GetSelfNodeId();

            var accentColor = (Color)Application.Current.Resources["PhoneAccentColor"];
            myNode.Tag = accentColor.Serialize();

            this.nodes.Add(myNode);
        }

You'll notice I'm using a helper method to abstract the serialization. Here are all the helper methods I'm using:

    using System;
    using System.Windows.Media;

    using Newtonsoft.Json;

    public static class ColorExtensions
    {
        public static string Serialize(this Color source)
        {
            return JsonConvert.SerializeObject(source);
        }
    }

    public static class StringExtensions
    {
        public static Color DeserializeAsColor(this string source)
        {
            try
            {
                return JsonConvert.DeserializeObject<color>(source);
            }
            catch (Exception exc)
            {
                return Colors.White;
            }
        }

        public static Brush DeserializeAsBrush(this string source)
        {
            return new SolidColorBrush(source.DeserializeAsColor());
        }
    }

I'm using Json.Net for the serialization as it's already referenced (as a dependency of SignalR) but you could use whatever you want.
The other important point of note is that I have exception handling in the Deserialize[AsColor] method. This is REALLY important as we may be receive messages from apps on other devices and they may be sending different, or no, information in the Tag. Be sure to handle deserialization exceptions and never make assumptions about what you'll receive.
This is of minor issue when you're only connecting to your own devices as it's easy to tell when not all devices are running the same version of the app-that's why the version number is auto-generated and displayed on the debug/config page.
It becomes more of an issue when connecting to the internet and anyone else is running a version of the app and also connecting to the internet as you'll be notified of each others nodes.

BTW: Always connect via the web option unless you really, really have a good reason to use UDP.
This is for 2 reasons: 1. We get to track what you're doing as everything that goes via the web is logged. (We'll make all the logged data available at the end of the project.) And 2. It's cool when other nodes (representing real other people/devices) appear in your garden.

Once we've got the color information in the tag we have to make sure we send it. So update MyVisualNode.cs:
this.gardener.UpdateSelfNodePosition(this.X, this.Y, this.Tag);

Then we need to make sure that we use the received color when drawing nodes.
In the Draw method of VisualNode.cs add:
this.NodeSize = Map(normalisedConnectedness, 0, 1, NodeSizeMin, this.NodeSizeMax());

this.Center.Fill = this.Tag.DeserializeAsBrush();
this.Center.Width = this.NodeSize;
this.Center.Height = this.NodeSize;


Simples.
Now just fire up a couple of emulators and voila:

If you try this over Web comms you'll notice that other nodes don't show up until they're moved. I think this is because of the way the WIEB (Where Is EveryBody) message is handled. Let me know if this is a big issue for you. Or fix it and send me a patch. ;)



But what if we want to test this easily without deploying to lots of devices? We'll just update the default nodes-that's what they're there for.

So in MainPage.xaml.cs

private void CreateDefaultNodes(int numberOfNodes)
{
    for (int i = 0; i < numberOfNodes; i++)
    {
        var visualNode = new VisualNode(this.rand, MainCanvas);
        visualNode.Tag = this.RandomAccentColor(this.rand).Serialize();

        this.nodes.Add(visualNode);
    }
}

private Color RandomAccentColor(Random random)
{
    // via http://chemicaloli.net/2011/10/01/windows-phone-7-accent-colour-list/
    switch (random.Next(1, 10))
    {
        case 1: return new Color { A = 255, R = 27, G = 161, B = 226 }; // Blue
        case 2: return new Color { A = 255, R = 160, G = 80, B = 0 }; // Brown
        case 3: return new Color { A = 255, R = 51, G = 153, B = 51 }; // Green
        case 4: return new Color { A = 255, R = 230, G = 113, B = 184 }; // Pink
        case 5: return new Color { A = 255, R = 162, G = 0, B = 255 }; // Purple
        case 6: return new Color { A = 255, R = 229, G = 20, B = 0 }; // Red
        case 7: return new Color { A = 255, R = 0, G = 171, B = 169 }; // Teal
        case 8: return new Color { A = 255, R = 162, G = 193, B = 57 }; // Lime
        default: return new Color { A = 255, R = 216, G = 0, B = 115 }; // Magenta
    }
}

The above is all quite trivial but hopefully shows how easy it is to send additional information. You can send ANYTHING so don't let yourself be limited by just thinking you're limited to accent colours.

Now go. And be creative...

0 comments:

Post a Comment

I get a lot of comment spam :( - moderation may take a while.