Category Archives: SignalR

The Beauty of Anonymity

No, not the kind commonly found on the Internet, which is often anything but. Rather, C#’s Anonymous types, introduced way back in 2007 with C# 3.0 and more often than not ignored by me until now.

I used to avoid JSON. Its syntax seemed just like every other delimited format I’d ever seen. In short, a headache to parse. XML is more verbose. Yet (at least for me) more humanly readable. That was until I found two things:

  1. XML parsing across different browsers is an even bigger headache
  2. With JSON I can create almost identical classes on both a C# server and a Javascript client

Later, the relationship was strengthened by the discovery of services like http://json2csharp.com/, which will create a C# class for you out of a JSON sample, as will more recent versions of Visual Studio. That’s great for dealing with someone else’s API output.

But sometimes a class seems like overkill. What if you have just one place where you really, really need to send just a bit of JSON to the client? For example, in one of my SignalR hubs, I need to send three things:

  1. brief message
  2. start time
  3. end time

SignalR, by the way, always sends JSON so all you need is some way to package (enclassulate?)  your data. Enter the Anonymous type, which if you use Linq you’ve probably already seen many times, e.g:

var productQuery =
    from prod in products
    select new { prod.Color, prod.Price };

foreach (var v in productQuery)
{
    Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}

With a var, the usage is basic: “var foo = new { someData = “something” };” Anonymous types support nesting too, so I was able to tidily separate my message data from my duration data. Here’s a sample method in the SignalR hub on the server:

public void AnonTypeDemo(string groupID)
{
    var startTime = DateTime.UtcNow;

    var endTime = startTime + TimeSpan.FromMinutes(10);

    // the anonymous type
    var data = new { message = "started", duration = new { startTime, endTime } };

    Clients.Group(groupID).anonTypeDemo(data);
}

The method sends a JSON string that looks like this:

{
    "message": "started",
    "duration": {
        "startTime": "2015-02-24T18:19:48.8008904Z",
        "endTime": "2015-02-24T18:29:48.8008904Z"
    }
}

On the client side, a Javascript method will be called by the SignalR client to process the received data:


myHub.client.anonTypeDemo = function (data) {

    console.log(data);

    var message = data.message;

    var startTime = data.duration.startTime;
    var endTime = data.duration.endTime;
}

And that’s all he wrote.

Real-time mapping with SignalR and a client app

A couple months ago I watched a live seminar given by @bradygaster and @jongalloway on SignalR. During their presentation, they demoed a web mapping app where all the attendees could share their locations with anyone viewing the web page. It occurred to me that capturing GPS data from a phone would be a good test of SignalR’s capabilities – namely how quickly and reliably it could transfer rapid bursts of data. I put together a web app with a Bing Map and a barebones Windows Phone client app that sends GPS data (lat, lon, altitude and speed) to a SignalR hub running in the web app.

Every time data is received the web map displays a red dot. Every 30th time it displays a custom marker showing altitude and speed. If the data is coming from the phone app, this translates into approximately every 300 meters. Using the apps while driving was interesting in that it graphically pointed out gaps in cellular data coverage between Helena and Missoula, Montana. While running or biking it was also interesting – to a certain extent. rtr2 I actually left a screen capture video running during one bike ride. On the descent I reached 48 mph. Yet reviewing even that portion of the video later was like watching paint dry. So if you’re thinking it would be exciting to watch your friends run in real time on a map, well. That isn’t to say that something like this doesn’t have applications. On the commercial side, while the developers of SignalR make no warranties to its suitability, an app running in the background could show delivery driver or utility worker locations. Or as your SWAT team is moving into position…uhm, in light of current events we won’t go there. Need to know where your kids are?

Personally, I’d like to be able to fire up an app, send an email to one or more friends to check a link so they know where to go to help me get an elk off some rocky, heavily timbered mountain (I actually sent a screenshot off the phone app to a friend more because I wanted someone to know where I was this year since it was -10F. In that situation, of course, the best and first thing I did was build a healthy fire).

In the interest of getting this post done, I’m not going to go through just a few of the project steps. I’d encourage anyone with Visual Studio to clone https://github.com/stonetip/realtimeRun, which contains the complete project code aside from Nuget packages (just do a restore). A couple of things:

  1. Run VS as Administrator, particularly if you’d like to create a virtual directory for the web app. This allows testing from a device, which is nice if you actually want to walk around within range of your wi-fi router.
  2. While it’s great to use the Location tab/map in a Windows Phone emulator, it’s a pain to get the emulator to see a local machine on your network. I recommend setting up a test site on Azure and deploy to that (you’ll need to configure the URL in the Windows Phone app to point to it). Then the emulator will work AND you can play with the app anywhere you have a wi-fi or cellular data connection.
  3. SignalR supports Websockets. IIS8 does too, but you’ll need to enable it under Windows Features:enable_ws

    If using Azure, you’ll need to visit the CONFIGURE section for the website inside the Portal and enable it. If you don’t feel like doing either, no big deal. Other protocols are fine. SignalR will intelligently pick one.

 

The Web App

Creating  a SignalR web app is fairly easy (provided you’ve added the requisite Nuget packages). In an Owin-based web app like this one, a single line added to the Startup.cs file will kick it off:

app.MapSignalR();

The next step is to create a Hub class. In my case, this is a simple class that receives data from a client and passes data to all clients that have the client method “broadcastLatLon”:

using Microsoft.AspNet.SignalR;

namespace RealtimeRun
{
    public class MapHub : Hub
    {
        // The Send method is used on a client to deliver data to the server
        public void Send(string lat, string lon, double? altitude, double? speed)
        {
            // In return, the server broadcasts the data to all clients.
            // Those that have the broadcastLatLon method will use the data on the client
            Clients.All.broadcastLatLon(lat, lon, altitude, speed);
        }
    }
}

That’s it for the backend code. Everything else in the web app is done in the client page, in this case map.aspx. One thing you’ll need is to provide a Bing Maps API key (info here).  You could, of course, adapt this sample to use Google Maps, HERE Maps or any other web mapping service. We’re just dealing with client and server methods that are sending text strings – nothing mysterious or requiring a masters in GIS.

Note that in this app I use a secondary config file, called AppSecrets.config to avoid using my key directly in Web.config and have that exposed in the Github repository (see http://www.mattburkedev.com/keep-your-azure-secrets-safely-out-of-git/). BTW, if you’re using Visual Studio with Git, I highly recommend using the Github VisualStudio.gitignore file. It’s chock full of exclusionary goodness. The Web.config file contains a commented-out section showing what the contents of AppSecret.config should look like.

The map.aspx file shows a map along with a convenient test button (which will place a random marker within a few kilometers of your location each time its clicked) and a line showing the coordinates, altitude and speed: web_app Once you get the app up and running, try clicking the daylights out it. This will give you an immediate visual indication that things are working. I won’t go into more detail on the map.aspx file. The code is well-commented.

Update

I was asked what would happen if more than one person (or even one person using multiple browsers or devices) was sending data. All of it would show up on the map. That’s because this sample doesn’t differentiate between clients. SignalR does provide support for distinct users. Check out Working with Groups in SignalR for more on this.

 

The Phone Client

This is about as barebones a client as you can get…just enough UI to let you know its working. A couple of things you need to do

  1. Open WMAppManifest.xml and on the Capabilities tab, make sure that ID_CAP_LOCATION is active.
  2. In App.xaml.cs, add these variables and change or add these two sections to:
    public static Geolocator Geolocator { get; set; }
    public static bool RunningInBackground { get; set; }
    
    private void Application_RunningInBackground(object sender, RunningInBackgroundEventArgs args)
    {
        RunningInBackground = true;
    }
    
    private void Application_Activated(object sender, ActivatedEventArgs e)
    {
        RunningInBackground = false;
    }
    

Next we’ll turn our attention to the MainPage.Xaml layout. All that’s needed is one UI element inside the LayoutRoot:


Just a simple TextBlock to tell us the GPS is working by displaying coordinates, altitude and speed. In MainPage.xaml.cs, after adding or restoring the Nuget packages,  there’s not a lot to do. The SignalR part is minimal: a couple private vars, a start method (which gets invoked in the MainPage ctor upon Loaded, and a send method, which I imaginatively named SendMessage.

public async Task StartSignalRHub()
{
    try
    {
        _hubConnection = new HubConnection(SiteUrl);

        _hub = _hubConnection.CreateHubProxy("MapHub");

        await _hubConnection.Start();
    }
    catch (Exception err)
    {
        Debug.WriteLine(err.Message);
    }
}

public void SendMessage(string lat, string lon, double? altitude, double? speed)
{
    try
    {
        _hub.Invoke("send", lat, lon, altitude, speed);
    }
    catch (Exception err)
    {
        Debug.WriteLine(err.Message);
    }
}

Similarly, the geolocation portion consists of a start method to fire it up (which gets invoked either upon loading or navigating to the page):

public void StartGeolocation()
{
    if (App.Geolocator != null) return;

    App.Geolocator = new Geolocator { DesiredAccuracy = PositionAccuracy.High, MovementThreshold = 10 }; // 10 meters (to limit data transmission)
    App.Geolocator.PositionChanged += Geolocator_PositionChanged;
}

private void Geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
    var roundedLat = Math.Round(args.Position.Coordinate.Latitude, 6);
    var roundedLon = Math.Round(args.Position.Coordinate.Longitude, 6);
    var altitude = args.Position.Coordinate.Altitude;
    var speed = args.Position.Coordinate.Speed != null && Double.IsNaN((double)args.Position.Coordinate.Speed) ? 0 : args.Position.Coordinate.Speed;

    Debug.WriteLine("{0}, {1} altitude: {2}, speed: {3}", roundedLat, roundedLon, altitude, speed);

    SendMessage(roundedLat.ToString(CultureInfo.InvariantCulture),
        roundedLon.ToString(CultureInfo.InvariantCulture), altitude, speed);

    Dispatcher.BeginInvoke(() =>
        TblockLatLonBlock.Text =
            String.Format("{0}, {1} altitude: {2}, speed: {3}", roundedLat, roundedLon, altitude, speed));
}

Note in StartGeolocation that the MovementThreshold has been set to 10 (meters). I did this because I wanted to generate a reasonable amount of data but not dump tons of points onto the map. Similarly, in the map.aspx page, a label is generated only every 30 points, so if the data is coming from the phone app that means about every 300 meters. As mentioned before, it’s usually easier to test and debug if you have published the website to Azure or anywhere capable of running the web app which has a resolvable domain name. If you have done the voodoo that allows your local machine to be visible to your Windows Phone emulators, that’s great too. I just never have time to mess with that.

Whatever you use, the SiteUrl string needs to be set to the root of that site, e.g. “http://myMapTest.azurewebsites.net/” so that the phone app has somewhere to send its data. When everything is set up, open a browser window to the map.aspx page. With the phone app as the startup project, launch it for debugging and try it out. Under Additional Tools in the emulator, if you select Location, you can set a series of points and then replay them. For example, I’ve create a reenactment of the historic canoe route from my former digs on Juanita Dr. in Kirkland, WA to Spud’s fish ‘n’ chips. (The return trip usually was much slower, but paddling hard alleviated our guilt over chowing down on all those fries):

juanita_dr

Hit play and watch the epic voyage unfold in the web browser: spuds