Search code examples
c#oopdesign-patternscommand-pattern

Using Command Pattern to send and receive commands over serial


I am working with a desktop app that should send and receive commands via serial with a firmware being programmed by my coworker.

We have devised a protocol where command types are represented by ASCII letters, each command type might contain a payload (which is arbitrary between command types, but fixed for each type), and commands are wrapped in square brackets. For example, we have the following commmands:

  • [S1234] -> Sent vrom the PC to the device to store a new serial number, or from the device to the PC to inform the current serial number (sort of a getter/setter command);
  • [R] - Sent from the PC to the device to ask for a "get serial" command;
  • [A12] - Sent from the the device to the PC to inform a new ADC reading;
  • [B] - Sent from PC to device to ask for a battery charge;
  • `[B89] - Sent from device to PC to inform battery charge;
  • Etc.

So I have a class that receives and parses the incoming bytes, and each time a command is successfully parsed, an event is raised, with the following tentative signature:

internal event EventHandler<SerialCommand> CommandReceived;

Where SerialCommand would have different subtypes: BatteryCommand, AdcCommand, SerialCommand, and others. Each command type is to be associated with its respective "char code".

My question is: how should the client code use this? For example, the current implementation for when I receive a command has a switch/case with hard-coded char literals, which I find very fragile and ugly:

    void CommandReceivedHandler(object sender, SerialCommand command)
    {
        switch (command.Code)
        {
            case 'A':
                int value= Convert.ToInt32(command.Value);
                _frameStreamer.AddFrame(new Frame<int>(new[] { value}));
                break;
            case 'B':
                BatteryLevel= (int)command.Value;
                break;
            case 'D':
                DoSomething((byte)command.Value);
                break;
            case 'S':
                SerialNumber = (int)command.Value;
                break;
        }           
    }

Currently, these "char codes" are spread around a bunch of classes, and if I ever need to change a given code, I would need to look around for every occurence (shotgun surgery anti-pattern).

What I need to do are two things:

  • Encapsulate char codes inside the very commands only, instead of client code;
  • Polymorphically execute actions at the client (CommandReceived event consumer) preferrably without the switch/case statement.

Solution

  • You can try something like this:

    public abstract class BaseCommand
    {
        //Code not needed at all, because logic encapsulated into command
        //public char Code { get; set; }         
        public abstract void Action(IClient client);
    }
    
    public abstract class BaseCommand<T> : BaseCommand
    {
        public T value { get; set; }
    }
    
    public class CommandA : BaseCommand<int>
    {            
        public override void Action(IClient client)
        {        
            client.someInt = value * 2;
        }
    }
    
    public class CommandB : BaseCommand<string>
    {
        public override void Action(IClient client)
        {
            client.someString = value.Trim();
        }
    }
    
    public interface IClient
    {
        void CommandReceivedHandler(object sender, BaseCommand command);    
        int someInt { get; set; }
        string someString { get; set; }
    }
    
    public class Client : IClient
    {
        public void CommandReceivedHandler(object sender, BaseCommand command)
        {
            command.Action(this);
        }
    
        public int someInt { get; set; }
        public string someString { get; set; }
    }