实战解析--主体实现和界面示意


    在上篇中细心的读者会发现以下两句是有问题的:
     _timer=new System.Timers.Timer(TIMER_INTRVAL);
     _timer.Elapsed
+=new System.Timers.ElapsedEventHandler(timer_Elapsed);

    就是因为这一句,我们的规则处理器的实例[比如数据线性转换f(x)=a.x+c ]哪怕启动两个也没有用,因为我们的容器是在单线程中执行,所以这两个规则处理实例的时间触发时刻非常接近。所以当第一个的时间点到时,第一个实例将会处理完队列中的所有待处理数据。等到第二个实例的处理代码得以执行时,队列可能已经是空的了。
    当然可以在new System.Timers.Timer(TIMER_INTRVAL)时加上一个随机数来让这两个实例的触发时刻分开;或者使用每个实例运行在单独的线程上真正互不干扰;或者使用事件通知而取消时间轮询。总之,技术上可以有很多办法达到一个目标。

    言归正传。我们的监控系统核心稳定的数据协议可以定义类似下面:

namespace OpenMonitor.Data {
    
    
using System;
    
using System.Collections;
    
using System.Text;
    
using System.Collections.Specialized;
    
using System.Xml.Serialization;

    [Serializable]
    [XmlRoot(
"DATA")]
    
public class InfoData:EventArgs{
        [XmlAttribute(
"ID")]
        
public string ID;

        [XmlAttribute(
"Time")]
        
public string Time;//HH:MM
        
        [XmlAttribute(
"Value")]
        
public string Value;
    
        [XmlAttribute(
"State")]
        
public string State;

        
public  InfoData() {
        }

        
public  InfoData(string id,object val) {
            ID
=id;
            Value
=val.ToString();
            DateTime dt
=DateTime.Now;
            Time
=dt.Hour.ToString("D2")+":"+dt.Minute.ToString("D2")+":"+dt.Second.ToString("D2");
            State
="?";
        }

        
        
        
public  InfoData(InfoData data) {
            
this.ID=data.ID;
            
this.Time=data.Time;
            
this.Value=data.Value;
            
this.State=data.State;
        }

        
public override string ToString() {
            
return "["+ID+"]("+this.State+")===>"+Value;
        }


    
    }

    
        
    [Serializable]
    [XmlRoot(
"DATA")]
    
public class DataPacket {
        [NonSerialized()]
        
private IDictionary ht;
            
        
public  DataPacket() {
            ht
=new System.Collections.Specialized.HybridDictionary();
        }

    
        [XmlArray(ElementName
="InfoDatas")]
        [XmlArrayItem(ElementName
="InfoData", Type=typeof(InfoData))]
        
public InfoData[] Infos {
            
get{
                InfoData[] rt
=new InfoData[ht.Count];
                
int i=0;
                
foreach(InfoData p in ht.Values) rt[i++]=p;
                
return rt;
            }

            
set {
                ht.Clear();
                
foreach(InfoData d in value) add(d);
            }

        }


        
public string this[string Key] {
            
get{
                
foreach(InfoData s in ht.Values) if(s.ID==Key) return s.Value;
                
return String.Empty;
            }

        }

        
public void add(InfoData info) {
            
if(ht.Contains(info.ID)) ht[info.ID]=info;
            
else ht.Add(info.ID,info);
        }

        
public int Count {
            
get {return ht.Count;}
        }

        
public void clear() {
            ht.Clear();
        }

    }


    
}


    这是这个系统比较稳定的地方,大量的通讯数据就是这样定义的,很简单吧。这些数据应该是从底往上走,我们再来看看从上往下发的控制命令的协议定义:

namespace OpenMonitor.Data {
    
    
using System;
    
using System.Collections;
    
using System.Text;
    
using System.Collections.Specialized;
    
using System.Xml.Serialization;

    [Serializable]
    [XmlRoot(
"DATA")]
    
public class CmdData:EventArgs{
    
        
public string Operator;

        
public string Time;

        
public string CommandName;
            
        
public string Command;
    
        
public  CmdData() {
        }

        
public  CmdData(string cmdName,string cmd,string op) {
            DateTime dt
=DateTime.Now;
            Time
=dt.Hour.ToString("D2")+":"+dt.Minute.ToString("D2")+":"+dt.Second.ToString("D2");
            CommandName
=cmdName;
            Operator
=op;
            Command
=cmd;
        }

        
        
        
public  CmdData(CmdData data) {
            
this.Operator=data.Operator;
            
this.CommandName=data.CommandName;
            
this.Command=data.Command;
            
this.Time=data.Time;
        }

        
public override string ToString() {
            
return "from "+Operator+"["+Time+"]===>"+Command;
        }

    
    }

    
    
}


    这些原始的数据和加工后的数据将要送到数据队列中,对于商业应用。我们可以选择IBM,MS等的产品。在此我们可以用一个简化的实现来看看核心的部分:
 1
 2namespace OpenMonitor.Utility {
 3    using System;
 4    
 5    using System.Collections;
 6    using OpenMonitor;
 7
 8    
 9    public    class SimpleQueue :IMessageQueue {
10        private IDictionary _catalogs = Hashtable.Synchronized(new Hashtable());
11        
12
13        private ILogService log;
14
15        private void init() {
16            log=DefaultLogger.INSTANCE;
17                        
18        }

19        public SimpleQueue() {
20            init();
21            DefaultLogger.INSTANCE.DEBUG("SimpleQueue created!");
22        }

23
24        
25        private Queue getQueue(string catalog) {
26            if (!_catalogs.Contains(catalog))
27                _catalogs.Add(catalog,Queue.Synchronized(new Queue()));
28            return _catalogs[catalog] as Queue;
29        }

30        public string[] getQueueNames() {
31            string[] rt=new string[_catalogs.Count];
32            int i=0;
33            foreach(string key in _catalogs.Keys) 
34                rt[i++]=key;
35            return rt;
36
37        }

38
39        public string[] getQueueInfo(string QueueName) {
40            Queue q=getQueue(QueueName);
41            object[] objs= q.ToArray();
42            string[] rt=new string[objs.Length];
43            for(int i=0;i<objs.Length;i++)
44                rt[i]=objs[i].ToString();
45            return rt;
46
47        }

48        public void addDataToQueue(string Catalog,object XmlData) {
49            getQueue(Catalog).Enqueue(XmlData);
50            //log.DEBUG(string.Format("addDataToQueue({0})",Catalog));
51        }

52        public object getDataFromQueue(string Catalog) {
53            object rt = null;
54            Queue q=getQueue(Catalog);
55            if (q.Count>0{
56                rt=q.Dequeue();
57            }

58            return rt ;
59        }

60    }

61}

62

    这个系统的主体模块是DataHub,以下是主要片断:




namespace OpenMonitor {
    
using System;
    
using System.Collections;
    
using System.Text;

    
using QPG.Net;
    
using OpenMonitor.Utility;
    
using OpenMonitor.Data;
    
using OpenMonitor.Services;


    
public class DataHub {
        
protected    SiteConfigPacket _cfg;//保存本地的采集点的配置信息

        
private IServerChannel _server;//接收数据采集器的通道
    
        
private QPG.Net.IClientChannel _client;//作为客户端报送数据的通道
        
        
private IMessageQueue _queue;

        
private ILogService log=DefaultLogger.INSTANCE;
        
        
private DefaultDataTransform dt;
        
protected    System.Collections.IDictionary _map;//用于索引信息点所在的通道号
        private lookForHandler _cmdFactory;

        
public event StateChangedCallback OnServerStateChanged;
        
public event DataReceivedCallback OnServerReceivedData;



        
public virtual lookForHandler CommandFactoryMethod{
            
get{return _cmdFactory;}
            
set{ _cmdFactory=value;}
        }


        
public virtual IServerChannel ServerChannel{
            
get{return _server;}
        }

        
public virtual IClientChannel ClientChannel{
            
get{return _client;}
            
set{_client=value;}
        }

        
        
public virtual IMessageQueue MQ{
            
get{return _queue ;}
        }

        
public virtual string HubCode{
            
get{return _cfg.SiteCode ;}
        
        }

        
public string HubName{
            
get{return _cfg.SiteName ;}
        
        }


        
public DataHub(IServerChannel collect_svr,IMessageQueue queue,    SiteConfigPacket cfg) {
            _server
=collect_svr;
        
            _queue
=queue;
            
            _cfg
=cfg;
            init();
            _map
=new Hashtable();
        
            log.DEBUG(
"DataHub "+HubName+" created!");
        }

        
public virtual void  sendCmd(string id,CmdData cmd) {
            
string  cmdtext=dt.toXml(cmd);
            
int cnum=getChannelNum(id);
            
if(cnum>0) _server.sendMsgToClient(cmdtext,cnum);
            
else _server.sendMsgToAll(cmdtext);
    }


        
public virtual void addMapItem(string id,int ChannelNum) {
            
if(_map.Contains(id)) _map[id]=ChannelNum;
            
else _map.Add(id,ChannelNum);

        }

        
public virtual int getChannelNum(string id) {
            
if(_map.Contains(id)) return (int)_map[id];
            
return 0;//不存在!

        }


        
        
        
private void init() {
            
//log.DEBUG("Memory Message Queue Started (SocketServerPort:"+_server.Port+")");
            _server.OnConnected+=new StateChangedCallback(OnConnected);
            _server.OnDisconnect
+=new StateChangedCallback(OnDisconnect);
            _server.OnReceived
+=new DataReceivedCallback(OnReceived);
            dt
=new DefaultDataTransform();
            _server.open();
        }


        
private void OnConnected(string text) {
            
if(OnServerStateChanged!=null) OnServerStateChanged(text);
        }


        
private void OnDisconnect(string text) {
            
if(OnServerStateChanged!=null) OnServerStateChanged(text);
        }

//        private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
//            string[] ServiceNames=_queue.getQueueNames();
//            for(int i=0;i<ServiceNames.Length;i++) {
//                Console.WriteLine(ServiceNames[i]+":");
//                string[] val=_queue.getQueueInfo(ServiceNames[i]);
//                for(int j=0;j<val.Length;j++) Console.WriteLine(val[j]);
//            }
//        }

        
private void handleInfoData(string text,int curSocketIndex) {
            DataPacket packet
=dt.toObject(text,typeof(DataPacket)) as DataPacket;
            InfoData[] data
=packet.Infos;
                
            
foreach(InfoData info in data) {
                addMapItem(info.ID,curSocketIndex);
                
if(_cfg[info.ID]==null||_cfg[info.ID].RuleConfigs.Length<1) _queue.addDataToQueue(ResultService.ServiceName,info);
                
else _queue.addDataToQueue(_cfg[info.ID].RuleConfigs[0].Name,info);
                    
            }

        }


        

        
private void handleData(string text, int curSocketIndex) {
            
if(text.IndexOf("InfoDatas")>=0) handleInfoData(text,curSocketIndex);
            
else if(text.IndexOf("CommandName")>=0{
                CmdData cmd
=dt.toObject(text,typeof(CmdData)) as CmdData;
                IDictionary dic
=InfoConfig.getParameterDictionary(cmd.Command);
                dic.Add(
"CurSocketIndex",curSocketIndex);
                
try{
                    IRule  hander
=CommandFactoryMethod(cmd.CommandName);
                    
if(hander!=null) hander.handle(this,dic);
                }

                
catch{
                    log.ERROR(text,
new Exception("找不到命令处理程序"));
                }

            }

        }

        
        
private void OnReceived(string text, int curSocketIndex) {
            
            
try{
                
string msg=string.Format("数据采集器({0})发来数据",curSocketIndex)+System.Environment.NewLine;
                
if(OnServerReceivedData!=null) OnServerReceivedData(msg+text,curSocketIndex);
                handleData(text,  curSocketIndex);
            }

            
catch{
                log.ERROR(text,
new Exception("不是完整XML"));
            }

            
        }

    }

}

    我们把DataHub加载到容器中,再给一个控件显示其最新收到的数据,效果如下:



    由于我们采用的是开放的XML+socket.所以任何符合上面格式的数据都可以把数据汇总到DataHub.
    DataHub收到数据后,就可以把数据进行加工处理,必要时要过滤,否则越到上面数据量就越多,对于没有变化的数据,我们为什么要耗费资源呢?
    订阅者收到数据后,可以发挥你的想象力了,下面是一个展示:


    今后的维护工作有两部分:一是增加已有类型的信息点,这很简单,修改配置文件如下:

<?xml version="1.0" encoding="utf-16"?>
<DATA xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  
<SiteCode>HN</SiteCode>
  
<SiteName>湖南</SiteName>
  
<InfoConfigs>
    
<InfoConfig ID="01" Code="A01">
      
<Name>信号1</Name>
      
<Rules>
        
<Rule Name="linearity">
          
<Parameters>a=30;k=0</Parameters>
        
</Rule>
        
<Rule Name="userscope">
          
<Parameters>NormalLow=100;NormalHigh=200</Parameters>
        
</Rule>
      
</Rules>
    
</InfoConfig>
    
<InfoConfig ID="02" Code="A02">
      
<Name>信号2</Name>
      
<Rules>
        
<Rule Name="linearity">
          
<Parameters>a=30;k=0</Parameters>
        
</Rule>
        
<Rule Name="sysscope">
          
<Parameters></Parameters>
        
</Rule>
      
</Rules>
    
</InfoConfig>
  
</InfoConfigs>
</DATA>

     但是如果增加了新的数据类型或者规则,那就要改程序了,也不难,看看我的线形处理器代码,保证你会写了:
 1namespace OpenMonitor.Services {
 2    using System;
 3    using System.Collections;
 4
 5
 6    using OpenMonitor.Data;
 7    using OpenMonitor.Utility;
 8
 9    
10    public class LinearityFormula:BaseService {
11    
12    
13        public LinearityFormula(IMessageQueue mq,SiteConfigPacket cfg):base("linearity",mq,cfg) {
14        
15        }

16        
17        
18        public override void handle(object data) {
19            InfoData info=new InfoData(data as InfoData);
20            string old=info.Value;
21            InfoConfig cfg=_cfg[info.ID];
22            
23            IDictionary d=InfoConfig.getParameterDictionary(cfg.getCurRule(this.ServiceName).Parameters);
24            double v=double.Parse(d["a"].ToString())*double.Parse(info.Value)+double.Parse(d["k"].ToString());
25            info.Value=v.ToString();
26            ToNextRule(info,cfg);
27            DefaultLogger.INSTANCE.DEBUG(this.GetType().Name+""+d["a"].ToString()+"×"+old+"+"+d["k"].ToString()+"-->"+info);
28    
29            }

30    }

31}

32

    好了,经过一周的努力,终于兑现了我给各位读者的承诺,请关注我的最后一篇<<总结>>吧。

 

posted @ 2005-11-29 16:22  成为-行动-拥有(BeDoHave)  阅读(2270)  评论(4编辑  收藏  举报