Lately I’ve been playing around with USB led lights in .Net. I wanted the¬†animations to be separated¬†from my code. Wouldn’t it be great if you could define¬†what animations¬†are executed in a JSON file? And map it to code?

Normally one would build a mapper that does the conversion of the JSON commands¬†to the methods. I like to use a more generic approach.¬†I’ve created a small utility class¬†that¬†executes commands by mapping and executing them as a method of the¬†object. The solution is workable for a host of scenarios:

  • You can use it to map a JSON RPC command from a webservice on an object, creating a more generic service.
  • You can now create a JSON file with USB led¬†animations specification that need to be executed when a certain event occurs.
  • Simplifying the mapping between commands and objects in a generic way.

Nuget

Before you start, there is a Nuget package. (Link to GitHub at the end of the blog.)

Install-Package KeesTalksTech.Utilities.Rpc»

Remote Timer Example

I’ll show you how things work using an example. Let’s create a remotely controlled timer. Commands on the timer are actions like¬†start and¬†stop.

namespace KeesTalksTech.Utilities.UnitTests.Rpc.Examples
{
    /// <summary>
    /// Indicates the object implements a timer.
    /// </summary>
    public interface IRemoteTimer
    {
        /// <summary>
        /// Gets a value indicating whether this instance is running.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is running; otherwise, <c>false</c>.
        /// </value>
        bool IsRunning { get; }

        /// <summary>
        /// Gets the ticks total.
        /// </summary>
        /// <returns>The ticks.</returns>
        int GetTicksTotal();

        /// <summary>
        /// Sets the interval.
        /// </summary>
        /// <param name="newInterval">The new interval.</param>
        void SetInterval(double newInterval);

        /// <summary>
        /// Starts this instance.
        /// </summary>
        void Start();

        /// <summary>
        /// Stops this instance.
        /// </summary>
        void Stop();
    }
}

Let’s implement the class:

using System;
using System.Timers;

namespace KeesTalksTech.Utilities.UnitTests.Rpc.Examples
{
    public class RemoteTimer : IRemoteTimer, IDisposable
    {
        private Timer timer;
        private int ticks = 0;

        /// <summary>
        /// Gets the interval.
        /// </summary>
        /// <value>
        /// The interval.
        /// </value>
        public double Interval { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="RemoteTimer" /> class.
        /// </summary>
        /// <param name="interval">The interval.</param>
        public RemoteTimer(double interval)
        {
            SetInterval(interval);
        }

        /// <summary>
        /// Sets the interval.
        /// </summary>
        /// <param name="interval">The interval.</param>
        public void SetInterval(double interval)
        {
            var isRunning = (timer?.Enabled).GetValueOrDefault();

            timer?.Stop();
            timer?.Dispose();

            timer = new Timer(interval);
            timer.Elapsed += (s, e) => ticks++;

            Interval = interval;

            if (isRunning)
            {
                Start();
            }
        }

        /// <summary>
        /// Starts this instance.
        /// </summary>
        public void Start()
        {
            timer.Start();
        }

        /// <summary>
        /// Stops this instance.
        /// </summary>
        public void Stop()
        {
            timer.Stop();
        }

        /// <summary>
        /// Gets a value indicating whether this instance is running.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is running; otherwise, <c>false</c>.
        /// </value>
        public bool IsRunning
        {
            get { return timer.Enabled; }
        }

        /// <summary>
        /// Gets the ticks total.
        /// </summary>
        /// <returns>
        /// The ticks.
        /// </returns>
        public int GetTicksTotal()
        {
            return ticks;
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            timer.Dispose();
        }
    }
}

Safety first

Classes¬†have many (inherited) methods. There are some methods you most likely never ever want to expose (like the¬†Dispose method).¬†That’s why the¬†Interpertation is always done on¬†an interface. This is especially important¬†when you are using this project as part of RPC construction.

To execute JSON commands we’ll need to create an¬†IInterpreter¬†based on an instance of the timer and the¬†IRemoteTimer interface:

var json = "";
var timer = new RemoteTimer(10);
var interpretor = Interpretation.Create<IRemoteTimer>(timer);

Executing a method

A method is defined as a JSON object with a method-name property. The system uses it to map it to a method.

/* Start the timer and assert it is running */
json = @"{ ""method-name"": ""Start"" }";
interpretor.Execute(json);

Assert.IsTrue(timer.IsRunning);

Got parameters?

Parameters are defined as properties as well. Just add their names and values to JSON:

/* Set a new interval: */
json = @"{ ""method-name"": ""SetInterval"", ""newInterval"": 20 }";

interpretor.Execute(json);
Assert.AreEqual(20, timer.Interval);

A series of methods

If you want to execute more than one method, just add the command in an array:

/* Stop the timer, set a new interval and restart it: */
json = @"[{ ""method-name"": ""Stop""},
          { ""method-name"": ""SetInterval"", ""newInterval"": 20 },
          { ""method-name"": ""Start""}]";

interpretor.Execute(json);
Assert.AreEqual(20, timer.Interval);

How about retrieving information?

Thought of that as well! Any results are returned as an object or an array of objects (in case of a series):

/* Get tick information from timer */
json = @"{ ""method-name"": ""GetTicksTotal""}";
var ticks = interpretor.Execute(json);

Assert.IsNotNull(ticks);
Assert.IsInstanceOfType(ticks, typeof(int));
Assert.IsTrue(Convert.ToInt32(ticks) > 0);

How about extension methods?

Extension methods are awesome. Let’s define a¬†Pause method:

namespace KeesTalksTech.Utilities.UnitTests.Rpc.Examples
{
    public static class IRemoteTimerExtensions
    {
        /// <summary>
        /// Pauses the specified timer.
        /// </summary>
        /// <param name="timer">The timer.</param>
        public static void Pause(this IRemoteTimer timer)
        {
            if (timer.IsRunning)
            {
                timer.Stop();
            }
            else
            {
                timer.Start();
            }
        }
    }
}

To allow extensions methods to be considered, they’ll need to be given to the¬†Create:

/* Pause the timer twice and assert running state */

var json = "";
var timer = new RemoteTimer(10);
var interpretor = Interpretation.Create<IRemoteTimer>(
    timer, 
    typeof(IRemoteTimerExtensions)
);

timer.Start(); 

json = @"{ ""method-name"": ""Pause"" }";
interpretor.Execute(json);
Assert.IsFalse(timer.IsRunning);

json = @"{ ""method-name"": ""Pause"" }";
interpretor.Execute(json);
Assert.IsTrue(timer.IsRunning);

Wrap up

The code has potential to solve a host of problems. If you like to fork it, please check GitHub and let me know what you think. The code used in the example can be found here.