AgvClient.cs 10.9 KB
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Web.Script.Serialization;

namespace Clinet
{
    public class AgvClient
    {
        private string _ip;           //远程IP地址
        private Socket client;        //客户端
        private int countRecon;       //重连次数
        private Timer timerRecon;
        private Timer timerSend;
        private Timer timerListen;
        private List<ClientNode> _node;
        private const int PORT = 12000;    //端口
        private readonly log4net.ILog LOG;

        /// <summary>
        /// 小车动作事件
        /// </summary>
        public delegate void ActionEvent(string name, string rfid);
        /// <summary>
        /// 小车到达,仅包装料仓
        /// </summary>
        public event ActionEvent Arrive;
        /// <summary>
        /// 小车已准备,对接完成
        /// </summary>
        public event ActionEvent Ready;
        /// <summary>
        /// 关门,仅包装料仓
        /// </summary>
        public event ActionEvent CloseDoor;
        /// <summary>
        /// 准备进入料架不能出料,仅包装料仓
        /// </summary>
        public event ActionEvent EnterShelf;


        public AgvClient(string serverIP, string logName = "AgvClient")
        {
            _ip = serverIP;
            LOG = log4net.LogManager.GetLogger(logName);
            _node = new List<ClientNode>();
            ThreadPool.SetMaxThreads(6, 6);  //线程池最大数量
        }

        public bool IsConn { private set; get; } = false;

        public void Connect()
        {
            countRecon = 0;
            IsConn = false;
            timerRecon = new Timer(Reconnect, null, 0, 5000);
        }

        public void Close()
        {
            if (timerRecon != null)
                timerRecon.Dispose();
            if (timerSend != null)
                timerSend.Dispose();
            if (timerListen != null)
                timerListen.Dispose();
            LOG.Info("AGV客户端关闭");
        }

        public void SetStatus(string name, string mark = "", string rfid = "", ClientAction action = ClientAction.None, ClientLevel level = ClientLevel.Low)
        {
            int idx = _node.FindIndex(s => s.Name == name);
            if (idx == -1)
            {
                ClientNode node = new ClientNode(name, mark, rfid, action, level);
                _node.Add(node);
                LOG.Info("SetStatus " + node.ToText());
            }
            else
            {
                _node[idx].Mark = mark;
                _node[idx].RFID = rfid;
                _node[idx].Action = action;
                _node[idx].Level = level;
                LOG.Info("SetStatus " + _node[idx].ToText());
            }
        }

        public void SetStatus(ClientNode node)
        {
            int idx = _node.FindIndex(s => s.Name == node.Name);
            if (idx == -1)
            {
                _node.Add(node.ToCopy());
                LOG.Info("SetStatus " + node.ToText());
            }
            else
            {
                _node[idx].Mark = node.Mark;
                _node[idx].RFID = node.RFID;
                _node[idx].Action = node.Action;
                _node[idx].Level = node.Level;
                LOG.Info("SetStatus " + _node[idx].ToText());
            }
        }




        private void Reconnect(object obj)
        {
            if (IsConn) return;

            Open();
            if (IsConn)
            {
                countRecon = 0;
                LOG.Info("连接AGV服务器成功");
                timerSend = new Timer(SendStatus, null, 50, 2000);
                timerListen = new Timer(ListenNet, null, 50, 100);
            }
            else
            {
                LOG.Info("连接服务器失败" + ++countRecon + "次");
            }
        }

        private void Open()
        {
            IsConn = false;
            try
            {
                if (CheckIP(_ip))
                {
                    client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 2000);
                    client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 2000);
                    client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, 1);
                    client.Connect(IPAddress.Parse(_ip), PORT);
                    IsConn = true;
                }
            }
            catch (Exception ex)
            {
                LOG.Error("Open", ex);
            }
        }

        private void ListenNet(object obj)
        {
            byte[] temp = new byte[200];

            try
            {
                if (client.Available > 0)
                {
                    int count = client.Receive(temp);
                    byte[] buff = new byte[count];
                    Array.Copy(temp, 0, buff, 0, count);

                    ClientNode node = Decode(buff);
                    if (node == null)
                    {
                        LOG.Info("命令解析失败: " + HexBuff(buff));
                    }
                    else
                    {
                        LOG.Info("来自服务器: " + node.ToText());
                        Resolve(node);
                        node.Action = ClientAction.Received;
                        Send(Encode(node));
                    }
                }
            }
            catch (Exception ex)
            {
                IsConn = false;
                LOG.Error("ListenNet", ex);
            }
        }

        private void SendStatus(object obj)
        {
            for (int i = 0; i < _node.Count; i++)
            {
                if (!IsConn) break;
                Thread.Sleep(50);
                byte[] buff = Encode(_node[i]);
                bool bln = Send(buff);
                if (!bln) IsConn = false;
            }
        }

        private void Resolve(ClientNode node)
        {
            if (node == null) return;

            switch (node.Action)
            {
                case ClientAction.Arrive:
                    LOG.Info("触发Arrive事件");
                    Arrive?.Invoke(node.Name, node.RFID);
                    break;
                case ClientAction.Ready:
                    LOG.Info("触发Ready事件");
                    Ready?.Invoke(node.Name, node.RFID);
                    break;
                case ClientAction.CloseDoor:
                    LOG.Info("触发CloseDoor事件");
                    CloseDoor?.Invoke(node.Name, node.RFID);
                    break;
                case ClientAction.EnterShelf:
                    LOG.Info("触发EnterShelf事件");
                    EnterShelf?.Invoke(node.Name, node.RFID);
                    break;
            }

        }

        private bool Send(byte[] buff)
        {
            if (!IsConn)
            {
                LOG.Info("Send 服务器没有连接");
                return false;
            }

            try
            {
                LOG.Debug("Send: " + HexBuff(buff));
                client.Send(buff);
                return true;
            }
            catch (Exception ex)
            {
                LOG.Error("Send", ex);
                return false;
            }

        }

        private byte[] Encode(ClientNode node)
        {
            try
            {
                System.Reflection.PropertyInfo[] info = node.GetType().GetProperties();
                string[] arr = new string[info.Length];
                for (int i = 0; i < info.Length; i++)
                    arr[i] = string.Format("\"{0}\":\"{1}\"", info[i].Name, info[i].GetValue(node));
                string json = "{" + string.Join(",", arr) + "}";
                LOG.Info("编码json " + json);
                byte[] buff = Encoding.UTF8.GetBytes(json);
                return buff;
            }
            catch (Exception ex)
            {
                LOG.Error("Encode", ex);
                return null;
            }
        }

        private ClientNode Decode(byte[] buff)
        {
            try
            {
                string json = Encoding.UTF8.GetString(buff);
                LOG.Info("解码json " + json);
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                Dictionary<string, object> dic = (Dictionary<string, object>)serializer.DeserializeObject(json);
                if (dic == null) return null;

                ClientNode node = new ClientNode();
                System.Reflection.PropertyInfo[] info = node.GetType().GetProperties();
                for (int i = 0; i < info.Length; i++)
                {
                    if (!dic.TryGetValue(info[i].Name, out object value))
                        continue;
                    switch (info[i].Name)
                    {
                        case "Action":
                            Enum.TryParse(value.ToString(), out ClientAction action);
                            value = action;
                            break;
                        case "Level":
                            Enum.TryParse(value.ToString(), out ClientLevel level);
                            value = level;
                            break;
                    }
                    info[i].SetValue(node, value);
                }
                return node;
            }
            catch (Exception ex)
            {
                LOG.Error("Decode", ex);
                return null;
            }
        }
        
        private string HexBuff(byte[] buff)
        {
            string s = "";
            if (buff == null) return s;

            for (int i = 0; i < buff.Length; i++)
                s += buff[i].ToString("X2") + " ";
            return s;
        }

        private bool CheckIP(string ip)
        {
            //IP合法
            string pattern = @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$";
            bool rtn = System.Text.RegularExpressions.Regex.IsMatch(ip, pattern);
            if (!rtn)
            {
                LOG.Info("非法的IP地址" + ip);
                return false;
            }

            //Ping服务端
            try
            {
                System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping();
                System.Net.NetworkInformation.PingReply result = ping.Send(ip, 2000);
                ping.Dispose();
                if (result.Status != System.Net.NetworkInformation.IPStatus.Success)
                {
                    LOG.Info("Ping " + ip + " 请求没有响应");
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                LOG.Error("CheckIP", ex);
                return false;
            }
        }

    }

}