基于WCF回调(WCF Callback)的GPS报警推送

2013年7月26日 分类: GPS系统

基于WCF回调(WCF Callback)的GPS报警推送

报警推送数据在很多软件中都有需求,比如任务提醒、消息广播、实时的监控报警等等。凡是对实时性要求越高的场景,越是需要服务器及时、准确地向客户端推送数据。一般的推送,我们可以选择使用socket,因为socket是双工通信的最佳模式。但是直接使用socket来开发,对于复杂的报警逻辑、权限判断、报警注册、数据库调用和更新处理来说,使用Socket处理,代码比较难以维护。

考虑到目前的基于部标808的GPS平台(需要完整平台源码的可以联系我购买),我们决定使用WCF来作为平台的基础服务架构,而WCF的回调模式可以满足GPS报警复杂的业务模式:

WCF推送报警WCF推送报警 已下载 903 次

1.注册
用户注册后,需要加载自己分配的功能权限和数据权限,功能权限决定了是否能看报警。
数据权限,决定了能看到那些报警,那些车辆的报警。

2.GPS报警发布
通常我们将808GPS服务器作为报警发布者,当接收到车辆GPS终端发送上来的报警后,发布给报警服务模块,由报警服务模块再根据逻辑转发给订阅者.

3.报警订阅
部标808协议规定了32路的报警再加上其他扩展的平台报警,可多达几十种报警,客户端需要通过订阅功能来接收自己感兴趣的报警。

4.报警过滤
报警最大的问题,不是如何实时的推送到客户端,而是如何避免误报。需要有一套算法设定来过滤掉无效的报警。频繁的误报,会对客户造成困扰,也会造成狼来了的效果,多次误报后,用户就失去了对报警的信任。如在工厂围墙的红外监控报警,报警设定的过于敏感,一有风吹草动就报警,保安就不得休息,时间长了就不看它,当有人非法翻越围墙的时候,反而没有看到。简单的过滤,就是时间过滤法,如当报警超过10秒后推送到客户端。

5.报警显示与处理
报警如何显示,如何避免重复显示,累积的未处理报警如何处理等等,这个也是个比较麻烦的用户体验的问题,很少有人去问问用户是否反感不断弹屏的功能设计。

基于WCF回调的双工通信,可以很好的完成报警推送。WCF中NetTcpBinding支持回调,因为从本质上讲TCP和IPC协议支持双向通信.

实现步骤:

1)首先定义报警服务接口(契约),提供订阅、注销、发布的外部接口功能。

namespace GpsNET
{
    /**
     * Gps报警推送服务
     */
    [ServiceContract(SessionMode=SessionMode.Required,
        CallbackContract=typeof(IGpsServiceCallback))]
    interface IGpsEventService
    {
        /**
         * 订阅
         * UserId 注册用户ID
         * Alarms 要订阅的报警类型ID
         * 注意IsOneWay = true,避免回调时发生死锁
         */
        [OperationContract(IsOneWay = true)]
        void Subscribe(int UserId, List<int> Alarms);
 
        //注销
        [OperationContract(IsOneWay = true)]
        void Unsubscribe(int UserId);
    }
}

  

2)定义GPS事件回调函数,当发生报警时,客户端会自动触发事件,关于IsOneWay = true这里就不多说了。

namespace GpsNET
{
    /**
     * 报警回调
     */
    public interface IGpsServiceCallback
    {
        /**
         * msgItems 接收到的报警事件集合
         */
        [OperationContract(IsOneWay = true)]
        void OnMessageReceived(List<AlarmItem> msgItems);
    }
}

  

3)报警服务实现

namespace GpsNET
{
    /**
     * Gps报警推送服务
     */
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
    internal sealed class GpsEventService:IGpsEventService
    {
        protected static log4net.ILog logger = log4net.LogManager.GetLogger(typeof(GpsEventService));
        public delegate void CallbackDelegate<T>(T t);
        //客户端的报警消息接收事件
        public static CallbackDelegate<List<AlarmItem>> MessageReceived;
        //订阅者
        public static List<AlarmSubscriber> Subscribers = new List<AlarmSubscriber>();
 
        //用户订阅报警,Alarms代表要订阅的报警类型
        public void Subscribe(int UserId, List<int> Alarms)
        {
            IGpsServiceCallback callback = OperationContext.Current.GetCallbackChannel<IGpsServiceCallback>();
            
            User u = GetUser(UserId);
            AlarmSubscriber subscriber =  GetSubscirber(UserId);
            if (subscriber == null)
            {
                subscriber = new AlarmSubscriber();
                subscriber.User = u;
                Subscribers.Add(subscriber);
                logger.Info("客户端" + UserId + "注册");
            }
            subscriber.Alarms = Alarms; //更新订阅
            subscriber.ClientCallback = callback;
            //绑定退出事件,在客户端退出时,注销客户端的订阅
            ICommunicationObject obj = (ICommunicationObject)callback;
            obj.Closed += new EventHandler(GpsEventService_Closed);
            obj.Closing += new EventHandler(GpsEventService_Closing);       
        }
 
        private AlarmSubscriber GetSubscirber(int UserId)
        {
            foreach(AlarmSubscriber sub in Subscribers)
            {
                if (sub.User.Id == UserId)
                    return sub;
            }
            return null;
        }
 
        private User GetUser(int UserId)
        {
            return new User(UserId);
        }
 
        void GpsEventService_Closing(object sender, EventArgs e)
        {
            logger.Info("客户端关闭退出...");
        }
 
        void GpsEventService_Closed(object sender, EventArgs e)
        {
            IGpsServiceCallback callback = (IGpsServiceCallback)sender;
            Subscribers.ForEach(delegate(AlarmSubscriber subscriber)
            {
                if (subscriber.ClientCallback == callback)
                {
                    Subscribers.Remove(subscriber);
                    logger.Info("用户" + subscriber.User.Id + "Closed Client Removed!");
                }
 
            });
            
        }
        //客户端断开
        public void Unsubscribe(int UserId)
        {
            IGpsServiceCallback callback = OperationContext.Current.GetCallbackChannel<IGpsServiceCallback>();
            
            Subscribers.ForEach(delegate(AlarmSubscriber subscriber)
            {
                if (subscriber.User.Id == UserId)
                {
                    Subscribers.Remove(subscriber);
                    logger.Info("用户" + subscriber.User.Id + "注销 Client Removed!");
                }
 
            });
        }
 
        //向客户端推送报警数据
        public static void SendAlarmMessage(List<AlarmItem> alarmItems)
        {
            //没有要推送的报警数据
            if (alarmItems.Count == 0)
                return;
 
            Subscribers.ForEach(delegate(AlarmSubscriber subscriber)
            {
                ICommunicationObject callback = (ICommunicationObject)subscriber.ClientCallback;
                if (((ICommunicationObject)callback).State == CommunicationState.Opened)
                {
                    try
                    {
                        //此处需要加上权限判断、订阅判断等
                        subscriber.ClientCallback.OnMessageReceived(alarmItems);
                    }
                    catch (Exception ex)
                    {
                        Subscribers.Remove(subscriber);
                        logger.Error("用户" + subscriber.User.Id + "出错:" + ex.Message);
                        logger.Error(ex.StackTrace);
                    }
                }
                else
                {
                    Subscribers.Remove(subscriber);
                    logger.Info("用户" + subscriber.User.Id + "Closed Client Removed!");
                }
            });
        }
       
        //通知用户服务已经停止
        public static void NotifyServiceStop()
        {
            List<AlarmItem> msgItems = new List<AlarmItem>();
            msgItems.Add(new AlarmItem(0,"Stop"));
            SendAlarmMessage(msgItems);
        }
    }
}

  4)客户端调用

public partial class Form1 : Form, GpsAlarm.<span style="color: #ff0000;"><strong>IGpsEventServiceCallback</strong></span>
   {
       int UserId = 1;
       public Form1()
       {
           InitializeComponent();
       }
 
       GpsAlarm.GpsEventServiceClient client;
       private void Form1_Load(object sender, EventArgs e)
       {
           try
           {
               client = new GpsAlarm.GpsEventServiceClient(new InstanceContext(this));//注意Form要实现接口
               //注册并订阅报警类型是1,2,3
               client.Subscribe(UserId, new int[]{1,2,3});
               listBox1.Items.Add("注册成功,等待消息推送");
           }
           catch (Exception ex)
           {
               listBox1.Items.Add(ex.ToString());
           }
       }
 
       #region IEventSystemCallback Members
       /**
        * 监听报警事件
        */
       public void OnMessageReceived(AlarmItem[] msgItems)
       {
           foreach (AlarmItem mi in msgItems)
           {
               listBox1.Items.Add(mi.Name);
           }
       }
       #endregion
   }

  

标签:
目前还没有任何评论.

Leave a Comment