应用系统端口的统一设计(一)

本设计不通用,只针对特定的一类问题。
如果系统经常和多种端口打交道,例如:COM,TCP,UDP,短信,打印机等等外接端口通讯,那么可以这样设计。

每种通讯方式都是类似,连接,打开端口,接收和发送数据,解析数据给系统。

所以可以统一起来进行设计,这样扩充端口更加容易。

使用C#语言描述

public delegate void GameEventDelegate(object sender, Action action);
//
public class Action : EventArgs
{
public PlayPort Receiver = PlayPort.Logic;

public Action(PlayPort sender, ICommand command)
{
Sender = sender;
Receiver = PlayPort.Logic;
Command = command;
}

public Action(PlayPort sender, PlayPort receiver, ICommand command)
{
Sender = sender;
Receiver = receiver;
Command = command;
}

public PlayPort Sender { get; set; }
public ICommand Command { get; set; }
}
//端口服务接口
public interface IPortService
{
event GameEventDelegate OnPort;
void Write(Action action);
}


Write函数:向设备写入数据
OnPort事件接收数据,并通知系统接收数据
Java代码和上面类似!

扩展接口可以这样

public class ControlPanelPort : IPortService
{
private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

private ConfigManager config;
private ControlPanelExecutor executor = new ControlPanelExecutor();
ControlPanelPortQueueManager controlPanelPortQueueManager = new ControlPanelPortQueueManager();
private IPersistService persistService;
public event EventHandler DataArrived;
public IDataParser Parser
{
set
{
controlPanelPortQueueManager.Parser = value;
controlPanelPortQueueManager.Start();
}
}

public Queue<byte[]> DataQueue = new Queue<byte[]>();

public ControlPanelPort(IPersistService persistService)
{
this.persistService = persistService;
config = ConfigManager.Instance(persistService.LoadConfig());
Init();
}

public SerialPort ControlPanelSerialPort { get; set; }

#region IPortService Members

public event GameEventDelegate OnPort;

/// <summary>
/// action中的data格式必须为int[]
/// </summary>
/// <param name="action"></param>
public void Write(Action action)
{
try
{
if (action.Receiver != PlayPort.ControlPanel) return;
if (ControlPanelSerialPort != null && ControlPanelSerialPort.IsOpen)
executor.Execute(this, action);
}
catch (IOException ex)
{
log.Error(ex.Message,ex);
if(ControlPanelSerialPort!=null)
ControlPanelSerialPort.Close();
}
}

#endregion

//
private void Init()
{
try
{
ControlPanelSerialPort = new SerialPort("control panel");
ControlPanelSerialPort.PortName = config[ConfigKey.控制面板串口];
ControlPanelSerialPort.BaudRate = 57600;
ControlPanelSerialPort.Handshake = Handshake.None;
ControlPanelSerialPort.DataBits = 8;
ControlPanelSerialPort.StopBits = StopBits.One;
ControlPanelSerialPort.Parity = Parity.None;
ControlPanelSerialPort.ReceivedBytesThreshold = 2;
ControlPanelSerialPort.WriteTimeout = SerialPort.InfiniteTimeout;
ControlPanelSerialPort.ReadTimeout = SerialPort.InfiniteTimeout;
ControlPanelSerialPort.DataReceived += controlPanelPort_DataReceived;
ControlPanelSerialPort.Open();
ControlPanelSerialPort.DiscardInBuffer();
ControlPanelSerialPort.DiscardOutBuffer();
//
controlPanelPortQueueManager.ControlPort = this;
}
catch (Exception ex)
{
log.Error("控制面板端口初始化失败!" + ex.Message);
}
}

private void controlPanelPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{

if (OnPort != null)
{
if (ControlPanelSerialPort == null) return;
Application.DoEvents();
Thread.Sleep(50);
byte[] data = new byte[ControlPanelSerialPort.BytesToRead];
ControlPanelSerialPort.Read(data, 0, data.Length);
log.Warn("<<<<接收控制面板信息,length:" + data.Length);
DataQueue.Enqueue(data);
if (DataArrived != null)
{
DataArrived(this, null);
}
}
}
catch (Exception ex)
{
log.Error(ex.Message, ex);
}
}

public void FireOnPort(Action action)
{
if(OnPort!=null)
{
OnPort(this, action);
}
}
}

其他所有的端口扩展都是类似的
在接受端口数据后,将数据直接放到处理队列里面,不要直接将处理代码放到这里,否则影响端口的继续工作,接收数据。不可以认为数据一次性都直接收完毕,数据可能是多次才能接受完成,所以处理的时候都要将数据放到队列里面,然后通过另一个线程处理。

数据接收函数
controlPanelPort_DataReceived
将数据放到队列中
DataQueue.Enqueue(data);
处理完毕后,触发事件,通知订阅者
OnPort(this, action);

Write数据到端口的执行者:ControlPanelExecutor
处理队列数据的管理者:ControlPanelPortQueueManager
持久化服务:IPersistService

其他端口都是类似处理!!!
下面将所有端口统一起来
public class PortProxy : IPortService
PortProxy类型是向前端表露的统一端口代理
他统一了所有端口的访问方式,这里使用了Proxy模式

public class PortProxy : IPortService
{
private static ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IPortService communicationPortService1;
private IPortService monitorPortService;
private IPortService controlPanelPortService1;
private IPortService printPortService1;
private IPortService tcpPortService1;
private IPortService sMSPortService;

public IPortService CommunicationPortService
{
set
{
communicationPortService1 = value;
communicationPortService1.OnPort += Event_OnPort;
}
}

public IPortService MonitorPortService
{
set
{
monitorPortService = value;
monitorPortService.OnPort += Event_OnPort;
}
}

public IPortService PrintPortService
{
set
{
printPortService1 = value;
printPortService1.OnPort += Event_OnPort;
}
}

public IPortService ControlPanelPortService
{
set
{
controlPanelPortService1 = value;
controlPanelPortService1.OnPort += Event_OnPort;
}
}
public IPortService TcpPortService
{
set
{
tcpPortService1 = value;
tcpPortService1.OnPort += Event_OnPort;
}
}

public IPortService SMSPortService
{
set
{
sMSPortService = value;
sMSPortService.OnPort += Event_OnPort;
}
}


#region IPortService Members

public event GameEventDelegate OnPort;

public void Write(Action action)
{
switch (action.Receiver)
{
case PlayPort.ControlPanel:
{
controlPanelPortService1.Write(action);
}
break;
case PlayPort.Printer:
{
printPortService1.Write(action);
}
break;
case PlayPort.Udp:
{
communicationPortService1.Write(action);
}
break;
case PlayPort.Tcp:
{
tcpPortService1.Write(action);
}
break;
case PlayPort.SMS:
{
sMSPortService.Write(action);
}
break;
case PlayPort.Monitor:
{
monitorPortService.Write(action);
}
break;
default:
Event_OnPort(this, action);
break;
}
}

#endregion

private void Event_OnPort(object sender, Action action)
{
if (OnPort != null)
OnPort(sender, action);
}
}


他代理了所有的端口,另外值得说明的是他充当了一个逻辑接口,该接口是一个虚拟的系统之间通信的接口,并不代表一个实际存在的接口,该接口的存在大大减少了系统模块之间的耦合性
应用的时候通过注入将可以将Port实例注入了

public abstract class AbstractGameState : IGameState
{
protected IPortService logic;
//
protected const PlayPort LOGIC_PORT = PlayPort.Logic;
protected const PlayPort CONTROL_PANEL_PORT = PlayPort.ControlPanel;
protected const PlayPort PRINTER_PORT = PlayPort.Printer;
protected const PlayPort SMS_PORT = PlayPort.SMS;
protected const PlayPort UDP_PORT = PlayPort.Udp;
#region IGameState Members

public IPortService PortService
{
set
{
logic = value;
logic.OnPort += Logic_OnPortEvent;
}
get
{
return logic;
}
}

protected abstract void Logic_OnPortEvent(object sender, Action action);
}

这里面的logic即使端口,他代表了所有的端口服务,通过它可以访问所有的端口,端口服务的客户访问大大简化,也与所有的具体端口松耦合,维护上大为简化

访问方法:

SingleCommand command = SingleCommand.Instance[CommandCode.指令];
command.Data = printParams;
logic.Write(new Action(PlayPort.Logic, PlayPort.Printer, command));

表示逻辑端口想打印机发送指令,进行打印
数据接收会触发OnPort事件
客户可以通过该事件进行程序处理

public abstract class AbstractSingleGameState:AbstractGameState
{
protected override void Logic_OnPortEvent(object sender, Action action)
{
//
if(GameContext.GameState==null) return;
if (GameContext.GameState.CurrentState == CurrentState)
{
try
{
ActionChainNode stateListener = ActionLockStateManager.Instance;
if (stateListener.Process(this, action)) return;
ThreadPool.QueueUserWorkItem(new WaitCallback(Doo),action);

OnPortEvent(sender, action);

}
catch (Exception ex)
{
log.Error(ex.Message,ex);
}
}
}

private static ActionChainNode globalChain = GlobalActionChainFactory.GetChain();

protected void Doo(object state)
{
//将不是立即执行的命令填出gamestate栈中,等到actionFilterChain的调用
ActionChainNode stackChain = GlobalActionStackFactory.GetChain();
stackChain.Process(this, (Action) state);
//
globalChain.Process(this, (Action)state);
}

protected virtual void OnPortEvent(object sender, Action action)
{
}
}

程序通过Logic_OnPortEvent处理程序进行接受指令的处理
这里使用了职责链模式进行所有的指令处理,下一节将写出该模式的设计

该处理函数既可以处理全局指令,也可以处理当前状态指令,并将事件进行传递。
传递到具体的处理类

public class StepWaitingState : AbstractSingleGameState
{
public override GameState CurrentState
{
get { return GameState.StepWaiting; }
}

public StepWaitingState()
{
}

public override object Enter(Action action)
{
}

protected override void OnPortEvent(object sender, Action action)
{
Doo(action);
}

private void Doo(object state)
{
Action action = (Action) state;
ActionChainNode controlPanelActionChain = WatingActionChainFactory.GetChain();
controlPanelActionChain.Process(this, action);
}

public override IGameState Transit(Action action)
{
if (GameContext.GameState.CurrentState == CurrentState)
GameContext.SetState(StateFactory.StepPost, action);
return GameContext.GameState;
}
}


如果接受的指令数据传递到当前状态后,通过该状态的职责链进行处理
ActionChainNode controlPanelActionChain = WatingActionChainFactory.GetChain();

整体就是这样了!

端口代理的实例化

<object id="PortProxy" type="Single.Core.PortProxy,Single.Core">
<property name="CommunicationPortService" ref="UdpPort"/>
<property name="PrintPortService" ref="PrintPort"/>
<property name="ControlPanelPortService" ref="ControlPanelPort"/>
<property name="TcpPortService" ref="TcpPort"/>
<property name="SMSPortService" ref="SMSPort"/>
<property name="MonitorPortService" ref="UdpMonitorPort"/>

</object>


PortProxy的注入

<object id="AbstractStateBase" abstract="true">
<property name="PortService" ref="PortProxy"/>
<property name="PersistService" ref="SinglePersistService"/>
</object>


综上,这样设计之后,客户在使用的过程中所有的细节都没有接触,只是统一通过PortProxy进行通信,所有的端口扩展也都通过ProtProxy进行扩充,不影响客户使用,扩展也更容易,端口的扩展继承统一的PortService,扩展方式统一!

原始链接
[url]http://qixin000.iteye.com/blog/1425135[/url]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值