Re-thinking the Roomware Socket Server

Posted on December 16, 2010

0


Brief

In 2009 I wrote the first version of the Roomware Socket Server, using Sockets and push-machanisms based on events to make stuff communicate to each other.

To discover services I proposed and implemented a scanner on mobile devices, scanning for Bluetooth devices that function as “beacons” connected to services. Other people found and implemented better ways. One year later, it is time or a review and redesign. Below you will find the different aspects that play a role.

Credits

  1. The Infrared5 team – demoing Brass Monkey in februari 2010 on FITC Amsterdam: an iPhone app to control an online Star Wars game. Brass Monkey pings IP-addresses within the WiFi network the device is connected to and connects to a IP-number when receiving the expected response.
  2. Jeff Winder – Using RemoteDroid and Air (find RemoteDroid home site here)
  3. Andy and Ben / VURB – for introducing the concept of “CSS for devices”
  4. James Burke and Peter Robinett – talking about the VURB Urbannode app and Bonjour – as I was contemplating a centralized approach
  5. Robert Gaal – mentioning WebSockets on Music And Bits beginning of 2010

Choice of name

As “Roomware” has no clear owner, it becomes a generic thing like “potato”. So “the Roomware Socket Server” is meaningless to continue with. It should be something like “Fafazazaa” with pay-off: “a Roomware Socket Server”.

Changes in approach

The “Gafazazaa” Roomware Socket Server first was a centralized thing you had to connect to via an IP number, using Sockets over port = “Not Port 80”, via a discoverable service or domain name.

Plain and short: that is stupid and outdated. Below you will find the new agenda:

  1. It can use port 80 and WebSockets – to allow access over networks with firewalls and restricted access
  2. Use of WiFi to discover services – Roomware Services can be discovered over WiFi
  3. Service-ping over WLAN – Similar to what Red5 does with Brass Monkey, using a decentralized DNS to get a fast redirect to the Host once an IP-address with device is found.
  4. Decentralized DNS for fast service discovery – Where there might be one central DNS to expose services and devices, each Roomware Client can act as a DNS host and take over. Each DNS client will offer a full list of – at that moment – dicovered services
  5. No distinction between Roomware Socket Client / Server – The main “FafaZazaa” app will contain both Client and Server API and can act as both, at the same time
  6. Simplification of communication protocols – using a CSS-like protocol to transmit settings / instructions

Roomware is local – use WiFi, idiot!

As most Roomware Services are local (you have to be in the space to see the effect of what you are doing) and most devices used will have WiFi, using WiFi instead of the Bluetooth Discovery Service I designed in 2009 as the main point of entry or any device is like a “Duh! Idiot” to myself for overlooking this.

Websockets and Port 80

As stated before, port 80 is always open. This makes this port the default choice when you want the services to work anywhere.

In most more corporate and closed environments any other port is usually closed, making communication between items impossible.

WebSockets operate on port 80 as one of the possible ports.

Ping for services

It is not too hard to write the code to ping for a service, based on the IP-range the machine is on. Simply traverse through the 256 values of the last digit of the IP-address (xxx.xxx.xxx.yyy <– traverse this number) until you find one that says “yes?” to your “hello?”.

Offer a decentralized DNS

Once you find a machine with a Roomware Service Host on it, it is not that hard to ask it for all the services it has discovered and direct you to the current Central DNS Host.

As we use Sockets to communicate to the Roomware Server, once connected, each other Client and Service will be updated immediately regarding the new Client that has connected. If this Client also offers services, it will expose those.

Roomware Client also can act as Server

The code to build a Roomware Server is actually quite compact. It involves about 5 classes to register Socket Clients, address the right targets and make sure replies on requests are sent back to the proper Client.

When “A” is connecting to another Client, Client “B”, the Roomware Server of “B” can act as the temporary DNS Host, sending the package with all the up-to-date Services back to Client “A”.

This is handy because…

When we talk about Clients like your hand-held, tablet or laptop, the only use it has is to help you in Fast Service Discovery. However: I do not want other people to use or exploit anything on my machine.

When building services, the story changes.

When a Roomware Device is both Client and Server, it can start hooking up to multiple machines and create Clusters, sending information to other Roomware Servers as needed, based on services or direct addressing.

Thinking in Services or Counterparts?

Currently, the Roomware Socket Server as designed by me uses the Event Subscriber / Event Dispatcher model. A Server normally does not expose what kind of objects it hosts.

The Server itself is “empty”. It only operates as a Hub for all communications between different devices and interfaces, using the afore-mentioned Event Subscriber / Dispatcher model.

“Services” like used in Bonjour extend this model by openly broadcast what interfaces they have to offer.

With the current model, each “Service” has a distinctive counterpart in the form of a Mediator. If this “Service” is a house – for instance – the Mediator has all the interfaces to manipulate stuff inside of that house.

You can make this model more granular and modular by creating a stack of Mediators for each object-type. Like:

  1. Mediator:Light – with color, brightness and direction as possible input
  2. Mediator:Door – with the instructions “open” and “close” and a status-report on the current state (opened / closed) and the history (overview of door-state in the past N-hours)
  3. Mediator:Heater – with the instructions “set temperature to..” and a status report on the current temperature and the temperature measured over the past N hours or days.

Each Mediator in this model will know what to do with the input-data and how to use it on the device.

For instance: when a light can only be set on/off, it will not use the color-setting.

Each device –  according to the Services-Discovery model – can expose itself using a specific protocol like:

<services>
   <Mediators>
       <Light>
          <Mediator type="Light_D01" id="ml_002"
               handles="color,on_off,brightness"
               dispatches="on_off_stateChange,brightnessChange,colorChange,stateChange"
               reports="on_off_state,curentBrightness,currentColor,state_history">
               <meta>
                   <location id="r004">Room 4</location>
                   <name>Fuzzy ball of light in left corner</name>
               </meta>
         </Mediator>
      </Light>
      <Door>
         <Mediator type="Door_D01" id="md_0123"
              handles="open_close"
              dispatches="open_closedChange"
              reports="open_closed_state,state_history"/>
      </Door>
   </Mediators>
</services>

Using events as done with the current Roomware Socket Server is in my opinion still a very good way to go, as we use a real-time system to push data in two directions.

With this possible Service Protocol (as we do not use any existing, we can make our own) we have:

  1. The type of Mediator – to resolve what Mediator to use on the Client Side. We use in the exmaple “Light_D01” and “Door_D01” as we can have several specific Mediators
  2. The id of the mediator – to communicate directly to that device or service instead of to all
  3. The handles – to tell the Client what can be done with that Mediator. A light – for instance – can have a dimmer and color-settings, but an also be limited to “on” and “off”
  4. The dispatchers – to tell the Client which events the Mediator dispatches from the specific device or service.
  5. The reports it can give – to tell the Client what type of information it can request.
  6. The meta-data – which can be anything. In our example we give the name of the object and the ID of the location it has in the map of the room and building.

Using CSS?

As proposed and (very likely used) in the VURB Urbannode solution, CSS plays a major role.

Looking at CSS in an HTML sheet, it can call an object in the DOM by its Class name (H1, TD, TABLE) or by a reference (class=”myObjectTypeCSSstyle”). One thing about CSS is this: it is to set a general style for a group of things. It is not by default something to actively influence things.

That JavaScript has made it possible to use CSS settings in an active manner is something that came as a bonus.

<Mediator CSSclass="lights,dimmableLights,colorLights"
      type="Light_D01" id="ml_002"
      handles="color,on_off,brightness"
      dispatchers="on_off_stateChange,brightnessChange,colorChange,stateChange"
      reports="on_off_state,curentBrightness,currentColor,state_history">
</Mediator>

We can use the properties of CSS to create and push settings in an easy and readable manner. It will also open the API for a more “HTML” type of approach to Roomware.

As we might want to address the same light with different styles, why not use a comma-separated list of options. In this way we can “layer” our styles like we do with Cascading Styles in HTML. Any item in that list is “inherited”.

So our style-sheet can look like this and combinations can be used on specific objects:

<style>
   .lights
   {
      power:on; // Light please
   }
   .dimmableLights
   {
       strenght:50%; // Not to much
   }
   .colorLighths
   {
      color:#ffff00; // Nice and yellow
   }
</style>

When you want to change the settings on all, or reset them all, you can simply apply the style sheet on the group and be done with it.

For any specific object, you could add an id.

<style>
   ml_002
   {
       color:ff0000; // fire truck red
       strength:100%; // nice and bright
       stroboscope:500ms; // blink every half-a second
   }
</style

You can make up any kind of Style element

As you can be the creator of the Mediators, you can add any type of style-element to it, like the “stroboscope” in the example above, or some kind of color-list to let the light traverse through.

In the end you are using the style-sheets to set specific values on the Object you are addressing. Like this:

myLight.lightColor=yourCSS.color;
myLight.lightStrength=yourCSS.strength;
myLight.stroboscopeSpeed=yourCSS.stroboscope;
// is zero / no stroboscope when no value received

Each time you send CSS values, it will resolve these to an object and then use the values set to change the values on the object.

When no value is found, no change is made.

Advantages of CSS

Sending only value-objects to a specific object takes away the need for a Mediator on the Client side.

CSS also has a very clear purpose: to change specific values on objects of a specific type.

Advantages of a Client Side Mediator

The Client Side Mediator is a representative of a Remote Object, like the door, or the light. When your communication is two-way (sending values to the object and receiving data from one or more objects) or you want to do stuff Object Oriented and directly via code a Client Side Mediator comes in handy for the following purposes:

  1. You can directly listen to events and changes on your Remote Object – so when something changes on the object somewhere in your physical space, it will notify your code Client Side via a Dispatched Event.
  2. You can directly manipulate your Remote Object – instead of by CSS changes, you simply call the API of that object directly

Code-example on a Client Side Mediator

The steps are as follows:

  1. Create the Client Side Mediator – Using a Simple Factory and the Services XML list with all required data you can create a (set of) Client Side Mediators
  2. Expose them in the Client Side GUI – Attached to each Mediator your app will have the required GUI-elements to display and manipulate the state of the objects
  3. Address the objects directly – Using the Mediator, you set values on that Mediator which are then broadcast to the appropriate node which will send the message or request to the actual Object.

The code would look something like this:

// We want to call the mediator directly for this example
var mediatorList:Dictionary=new Dictionary();

// Get the mediators
for each (mediatorItem in serviceList)
{
    mediatorList[mediatorItem.id] = 
        SimpleMediatorFactory.getMediator(mediatorItem)
}

// Get the specific Mediator
var myLight:MediatorLight=mediatorList["ml_002"];
myLight.color=0xFF0000; // set to yellow
myLight.strength=50; // 50% strenght
// Set event listener for change in light strenght
myLight.addStrenghtChangeListener(myBrightnessChangeCallback);

Using code, Event Listeners and “direct access” to a remote object allows you to get the maximum out of a Roomware setup. As speed, bandwidth and latency allows.

Dynamically Constructed Mediators

When using Java, ActionsScript 3, C# or any other OOP based language, you very likely love to work with Classes with clear Interfaces. This also requires the Mediators to be pre-defined in the interface.

To send settings to any object, the CSS option is one way to work with this. And when we are only using our device as a remote-control with a very clear purpose we might be able to settle with that.

Here is a way to create a Client Side Mediator, using the Services data and CSS as a starting point:

<Mediator CSSclass="lights,dimmableLights,colorLights"
    type="Light_D01" id="ml_002">
      <instructions>
          <instruction name="light on" style="power:on;"/>
          <instruction name="dim light to 50%" style="strength:50%;"/>
          <instruction name="set light to yellow" style="color:#ffff00;"/>
      </instructions>
</Mediator>

Now, setting values is no fun if you can not chain stuff.

<Mediator CSSclass="lights,dimmableLights,colorLights"
    type="Light_D01" id="ml_002">
      <instructions>
          <instruction name="light on, 50%, yellow" style="power:on;strength:50%;color:#ffff00"/>
          <instruction name="flash light red - ALARM" style="strength:100%;stroboscope:500ms;color:#ff0000;"/>
          <instruction name="flash light faster - ALARM" style="strength:100%;stroboscope:10ms;color:#ff0000;"/>
          <instruction name="loop through colors" style="colorloop:#ffff00 #ff0000 #00ff00 #0000ff;"/>
      </instructions>
</Mediator>

Limitations

As said: there are some limitations to this. When you have a dynamically created interface to an objects, the communication-options are limited. (No- or very basic or generic feedback).

Streaming data – reducing overload

In some real-time scenarios, you will deal with real-time data that is constantly changing. For instance: when building your own controllers for a game using XBee / bluetooth devices and sensors.

Streaming data can clog up your network and slow down your CLient Side applications when too much overhead and translation is done and required.

The most efficient way to deal with streaming data is by using byte-arrays. Each data-point is located on a fixed place in the stream of data and each block is separated by a terminator, to make sure we read the data properly.

The data can be split up as an array (in the case of Clear Text or byte-data) or simply be read by position. Both have advantages and disadvantages. The main advantage is this: Speed / Reduction of required clock cycles.

The main disadvantage is this: rigid setup.

When you look at XML, every item has a name and/or identity – either via the node-name or via the “name” or “id” attribute you can add. When you look at a byte-array or text-array (text-array here displayed, with the “|” as separator) we get something like this:

myStringArray="john|Doe|1234|ff00ff|45|Gargoyle;Joe|Black|231|FFFF00|Cat;"

We split this raw data via:

// Get lines
myArray=myStringArray.split(";")

// Add a for / next loop in your imagination
// Get items
mySubArray=String(myArray[iterator]).split("|");
myPersonObject=Persons.getPerson(mySubArray[2])
myPersonObject.name=mySubArray[0];
myPersonObject.color=mySubArray[3];

Although not the fastest solution ever on this earth and just an example, we skip a lot of steps done otherwise when parsing XML. We also reduce the load on the network dramatically by leaving everything out that we do not need, including XML tagging and variable names.

Scanning for service providers

Scanning for service-providers is done by running through the available IP nubers in the current domain and see of one answers.

The WiFi and the DNS usually are defined at 192.168.0.1. The machines added to the network usually start from 192.168.0.2. Even though most internal networks and LANs work within this range you can not assume they always will.

The stes are as follows:

  1. Connect to the WiFi – to get into the network that contain the Roomware service(s)
  2. Find out the IP range – by resolving your own IP number
  3. Start scanning for other machines – by iterating through the IP range
    1. Ping for an answer – and connect to the machine if we get a “yes?”
  4. Get the DNS list – to find the main server
  5. Connect to the main server – and start cooking

The code might look something like this:

// Precondition: We are connected to the network
// 1: Get the IP number
var myIPnumber:String=getMyIpNumber();

// Get the first three digits: xxx.yyy.zzz.
var firstPartOfIP:String=myIPnumber.substr(0,myIPnumber.lastIndexOf("."));

var mainHostIP:String="";

var service:RoomwareService;

// Loop through the IPnumbers and ping for a service
for(var i=0;i<256;i++)
{
    // Get the IP number to scan, adds the String value of i
    // to the digits. Like this: "192.168.0." + i
    scanIPnumber=firstPartOfIP+String(i);

    // If it is not us - scan!
    if(scanIPnumber!=myIPnumber)
    {
       service = pingForService(scanIPnumber )

       if(service.found)
       {
          // We already have the service

          // So lets do something with it
          getPortableDNSdata(service);

          // Get IP address of current main host
          mainHostIP=service.getMainHostIP();

          // Connect to the main host
          // and make yourself known
          connectToMainHost(mainHostIP);

          // Done 
          Break;
       }
    }
}
// Start cooking

Each Client can be a DNS Host

To be a DNS Host means you keep a list of all currently connected devices and update the list regularly when an object logs off.

The steps could be this:

  1. Check – Run through the list of known IP-addresses
  2. Ping – if they are still the same / still active
  3. Clean up DNS – Remove the inactive IP addresses
    1. Remove data – Remove the Service-data
  4. Update – the other Clients with the new list

When a new client calls for contact, we can do the following:

  1. Register – the Client, the IP number and the services offered
  2. Update – the other Clients with the new list

Since we are running an Event Driven System anyway..

Since we run an Event Driven System anyway with the “WhatsmaCallit” Roomware Client / Server, we can use that exact same protocol to inform and update all connected Roomware Clients on the new DNS list (including all the available Services)

And done

This summarizes the main elements of what I have in mind with the new “Whatever” Roomware Socket Server.

Advertisements
Posted in: 4: Concepts