使用命令模式通过串口发送和接收命令
Using Command Pattern to send and receive commands over serial
我正在使用一个桌面应用程序,它应该通过串行发送和接收命令,固件由我的同事编程。
我们设计了一个协议,其中命令类型由 ASCII 字母表示,每个命令类型可能包含一个有效负载(在命令类型之间是任意的,但对每种类型都是固定的),并且命令包含在方括号中。例如,我们有以下命令:
[S1234]
-> 从 PC 发送到设备以存储新序列号,或从设备发送到 PC 以通知当前序列号(类似于 getter/setter 命令) ;
[R]
- 从 PC 发送到设备以请求 "get serial" 命令;
[A12]
- 从设备发送到 PC 以通知新的 ADC 读数;
[B]
- 从 PC 发送到设备以请求电池充电;
- `[B89] - 从设备发送到 PC 以通知电池充电;
- 等等
所以我有一个 class 接收和解析传入的字节,每次成功解析命令时,都会引发一个事件,具有以下暂定签名:
internal event EventHandler<SerialCommand> CommandReceived;
其中 SerialCommand
会有不同的子类型:BatteryCommand、AdcCommand、SerialCommand 等。每个命令类型都与其各自的 "char code".
相关联
我的问题是:客户端代码应该如何使用它?例如,当我收到命令时的当前实现有一个带有硬编码字符文字的 switch/case,我发现它非常脆弱和丑陋:
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;
}
}
目前,这些 "char codes" 散布在一堆 classes 周围,如果我需要更改给定的代码,我需要四处寻找每一个发生的地方(shotgun surgery anti -模式).
我需要做两件事:
- 仅在命令中封装字符代码,而不是客户端代码;
- 多态地在客户端(
CommandReceived
事件消费者)执行操作,最好不要使用 switch/case 语句。
您可以尝试这样的操作:
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; }
}
我正在使用一个桌面应用程序,它应该通过串行发送和接收命令,固件由我的同事编程。
我们设计了一个协议,其中命令类型由 ASCII 字母表示,每个命令类型可能包含一个有效负载(在命令类型之间是任意的,但对每种类型都是固定的),并且命令包含在方括号中。例如,我们有以下命令:
[S1234]
-> 从 PC 发送到设备以存储新序列号,或从设备发送到 PC 以通知当前序列号(类似于 getter/setter 命令) ;[R]
- 从 PC 发送到设备以请求 "get serial" 命令;[A12]
- 从设备发送到 PC 以通知新的 ADC 读数;[B]
- 从 PC 发送到设备以请求电池充电;- `[B89] - 从设备发送到 PC 以通知电池充电;
- 等等
所以我有一个 class 接收和解析传入的字节,每次成功解析命令时,都会引发一个事件,具有以下暂定签名:
internal event EventHandler<SerialCommand> CommandReceived;
其中 SerialCommand
会有不同的子类型:BatteryCommand、AdcCommand、SerialCommand 等。每个命令类型都与其各自的 "char code".
我的问题是:客户端代码应该如何使用它?例如,当我收到命令时的当前实现有一个带有硬编码字符文字的 switch/case,我发现它非常脆弱和丑陋:
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;
}
}
目前,这些 "char codes" 散布在一堆 classes 周围,如果我需要更改给定的代码,我需要四处寻找每一个发生的地方(shotgun surgery anti -模式).
我需要做两件事:
- 仅在命令中封装字符代码,而不是客户端代码;
- 多态地在客户端(
CommandReceived
事件消费者)执行操作,最好不要使用 switch/case 语句。
您可以尝试这样的操作:
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; }
}