patrick
2019-12-10 1c4426810c71eead57084be8a18ade8d314dd8c4
src/Bro.Device.AuboRobot/AuboRobotDriver.cs
@@ -1,6 +1,8 @@
using Bro.Common.Base;
using Bro.Common.Helper;
using Bro.Common.Interface;
using Bro.Common.Model;
using Bro.Common.Model.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -8,13 +10,14 @@
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using static Bro.Common.Helper.EnumHelper;
namespace Bro.Device.AuboRobot
{
    [Device("AuboRobot", "奥博机器人", EnumHelper.DeviceAttributeType.Device)]
    public class AuboRobotDriver : DeviceBase
    public class AuboRobotDriver : DeviceBase, IMonitor
    {
        public Action<RobotMsg> OnMsgReceived { get; set; }
        public Action<DateTime, AuboRobotDriver, RobotMsg> OnMsgReceived { get; set; }
        AuboRobotInitialConfig IConfig
        {
@@ -32,9 +35,11 @@
        protected override void Init()
        {
            oldValues = new List<int>();
            if (string.IsNullOrWhiteSpace(IConfig.EndChar))
            {
                throw new ProcessException("协议文本的结束字符不可为空,请检查配置", null);
                throw new ProcessException("协议文本的结束字符不可为空,请检查配置");
            }
            client = new TcpClient();
@@ -42,6 +47,10 @@
            client.Client.Blocking = true;
            client.NoDelay = true;
            client.BeginConnect(IPAddress.Parse(IConfig.RobotIP), IConfig.RobotPort, OnConnect, null);
            replyHandleDict = new Dictionary<int, AutoResetEvent>();
            replyErrorCodeDict = new Dictionary<int, int>();
            taskHandleDict = new Dictionary<int, ManualResetEvent>();
        }
        protected override void Pause()
@@ -52,19 +61,38 @@
        {
        }
        RobotMsg scanMsg = new RobotMsg();
        protected override void Start()
        {
            //Query Robot IOs
            //SendMsg(RobotMsgType.Send, 0, true, RobotMsgAction.IO, RobotMsgParas.Query, new List<string>());
            scanMsg = new RobotMsg();
            scanMsg.Action = RobotMsgAction.IOQuery;
            Task.Run(() =>
            {
                Monitor();
            });
        }
        protected override void Stop()
        {
            client.Close();
            replyHandleDict.Values.ToList().ForEach(h => h.Set());
            replyHandleDict.Clear();
            taskHandleDict.Values.ToList().ForEach(h => h.Set());
            taskHandleDict.Clear();
            if (client != null && client.Connected)
            {
                client.Close();
            }
        }
        #endregion
        TcpClient client = new TcpClient();
        NetworkStream stream = null;
        byte[] buffer = new byte[1024];
        private void OnConnect(IAsyncResult ar)
@@ -72,41 +100,56 @@
            try
            {
                client.EndConnect(ar);
                client.GetStream().BeginRead(buffer, 0, buffer.Length, OnDateReceived, null);
                stream = client.GetStream();
                stream.BeginRead(buffer, 0, buffer.Length, OnDataReceived, null);
            }
            catch (Exception ex)
            {
                OnLog?.Invoke(DateTime.Now, this, ex.GetExceptionMessage());
                //OnLog?.Invoke(DateTime.Now, this, ex.GetExceptionMessage());
                LogAsync(DateTime.Now, "连接异常", ex.GetExceptionMessage());
                if (client != null && client.Connected)
                {
                    client.Close();
                }
                client.BeginConnect(IPAddress.Parse(IConfig.RobotIP), IConfig.RobotPort, OnConnect, null);
            }
        }
        private void OnDateReceived(IAsyncResult ar)
        private void OnDataReceived(IAsyncResult ar)
        {
            int dataLength = client.GetStream().EndRead(ar);
            if (dataLength > 0)
            try
            {
                byte[] data = buffer.Take(dataLength).ToArray();
                string dataStr = System.Text.Encoding.ASCII.GetString(data).Trim();
                AnalyzeData(dataStr);
                client.GetStream().BeginRead(buffer, 0, buffer.Length, OnDateReceived, null);
            }
            else
            {
                if (!client.Connected)
                int dataLength = stream.EndRead(ar);
                if (dataLength > 0)
                {
                    OnLog?.Invoke(DateTime.Now, this, "返回空数据,连接中断");
                    client.BeginConnect(IPAddress.Parse(IConfig.RobotIP), IConfig.RobotPort, OnConnect, null);
                    byte[] data = buffer.Take(dataLength).ToArray();
                    string dataStr = System.Text.Encoding.ASCII.GetString(data).Trim();
                    AnalyzeData(dataStr);
                    stream.BeginRead(buffer, 0, buffer.Length, OnDataReceived, null);
                }
                else
                {
                    if (!client.Connected)
                    {
                        //OnLog?.Invoke(DateTime.Now, this, "返回空数据,连接中断");
                        LogAsync(DateTime.Now, "连接中断", "返回空数据,连接中断");
                        client.BeginConnect(IPAddress.Parse(IConfig.RobotIP), IConfig.RobotPort, OnConnect, null);
                    }
                }
            }
            catch (Exception ex)
            {
                //OnLog?.Invoke(DateTime.Now, this, $"{Name}:{ex.GetExceptionMessage()}");
                LogAsync(DateTime.Now, "数据接收异常", ex.GetExceptionMessage());
            }
        }
        string msg = "";
        static object analyzeLock = new object();
        object analyzeLock = new object();
        private async void AnalyzeData(string data)
        {
            await Task.Run(() =>
@@ -137,25 +180,58 @@
            await Task.Run(() =>
            {
                Array.ForEach(datas, data =>
                 {
                     RobotMsg msg = RobotMsg.GetRobotMsg(data, IConfig.Seperator);
                {
                    RobotMsg msg = RobotMsg.GetRobotMsg(data, IConfig.Seperator);
                     if (msg.Type == RobotMsgType.Rec)
                     {
                         if (replyHandleDict.ContainsKey(msg.ID))
                         {
                             replyHandleList.Remove(msg.ID);
                    if (msg.Type == RobotMsgType.Rec)
                    {
                        replyErrorCodeDict[msg.ID] = msg.Para1;
                        if (replyHandleDict.ContainsKey(msg.ID))
                        {
                            replyHandleDict[msg.ID].Set();
                        }
                    }
                    else
                    {
                        if (msg.Action == RobotMsgAction.IOQuery)
                        {
                            newValues = new List<int>();
                             replyHandleDict[msg.ID].Set();
                             replyHandleDict.Remove(msg.ID);
                         }
                     }
                     else
                     {
                         SendMsg(RobotMsgType.Rec, msg.ID, false);
                         OnMsgReceived?.Invoke(msg);
                     }
                 });
                            for (int i = msg.Para1.ToString().Length - 1; i >= 0; i--)
                            {
                                newValues.Add((msg.Para1 >> i) & 1);
                            }
                            MonitorHandle.Set();
                        }
                        else
                        {
                            canMonitor = true;
                            switch (msg.Action)
                            {
                                case RobotMsgAction.Move:
                                    CurrentPoint = msg.Point;
                                    break;
                                //case RobotMsgAction.Load:
                                //    //SlotIndex = msg.Para1;
                                //    break;
                                case RobotMsgAction.GetPosition:
                                    CurrentPoint = msg.Point;
                                    break;
                                default:
                                    break;
                            }
                            if (taskHandleDict.ContainsKey(msg.ID))
                            {
                                taskHandleDict[msg.ID].Set();
                            }
                            LogAsync(DateTime.Now, "指令反馈", msg.GetDisplayText());
                            OnMsgReceived?.BeginInvoke(DateTime.Now, this, msg, null, null);
                        }
                    }
                });
            });
        }
@@ -164,7 +240,7 @@
        {
            get
            {
                if (sid > 999)
                if (sid > 99)
                {
                    sid = 1;
                }
@@ -173,49 +249,311 @@
            }
        }
        List<int> replyHandleList = new List<int>();
        Dictionary<int, AutoResetEvent> replyHandleDict = new Dictionary<int, AutoResetEvent>();
        Dictionary<int, int> replyErrorCodeDict = new Dictionary<int, int>();
        Dictionary<int, ManualResetEvent> taskHandleDict = new Dictionary<int, ManualResetEvent>();
        public void SendMsg(RobotMsgType type, int replyId, bool isWaitReply = true, RobotMsgAction action = RobotMsgAction.Move, RobotMsgParas para1 = RobotMsgParas.None, List<string> paras = null)
        public void SendReplyMsg(int replyId)
        {
            RobotMsg msg = new RobotMsg();
            msg.Type = type;
            if (msg.Type == RobotMsgType.Send)
            {
                msg.ID = SID;
            }
            else
            {
                msg.ID = replyId;
            }
            msg.Type = RobotMsgType.Rec;
            msg.ID = replyId;
            msg.Para1 = para1;
            msg.Paras = new List<string>(paras ?? new List<string>());
            SendMsg(msg, isWaitReply);
            SendMsg(msg, false);
        }
        public void SendMsg(RobotMsg msg, bool isWaitReply = true)
        bool canMonitor = true;
        object monitorLock = new object();
        public void SendMsg(RobotMsg msg, bool isWaitReply = true, bool isMonitorMsg = false)
        {
            replyErrorCodeDict[msg.ID] = 0;
            if (isWaitReply)
            {
                replyHandleList.Add(msg.ID);
                replyHandleDict[msg.ID] = new AutoResetEvent(false);
            }
            byte[] bytes = msg.GetMsgBytes(IConfig.Seperator, IConfig.EndChar);
            client.GetStream().Write(bytes, 0, bytes.Length);
            byte[] bytes = msg.GetMsgBytes(IConfig.Seperator, IConfig.EndChar, out string dataStr);
            if (!isMonitorMsg)
            {
                LogAsync(DateTime.Now, "指令发送", dataStr);
            }
            bool isNotTimeout = true;
            int reTryTime = IConfig.TimeoutRetryTimes;
            do
            {
                lock (monitorLock)
                {
                    if (!isMonitorMsg)
                    {
                        canMonitor = false;
                    }
                    if (isMonitorMsg && !canMonitor)
                        return;
                    stream.Write(bytes, 0, bytes.Length);
                    if (!isMonitorMsg)
                    {
                        LogAsync(DateTime.Now, "数据发送", BitConverter.ToString(bytes));
                    }
                }
                if (isWaitReply)
                {
                    int errorCode = replyErrorCodeDict[msg.ID];
                    if (errorCode != 0)
                    {
                        var desc = IConfig.RobotReplyWarnings.FirstOrDefault(u => u.WarningCode == errorCode);
                        throw new ProcessException($"{Name}{msg.ID}任务反馈异常{errorCode},异常描述:{(desc == null ? "无" : desc.WarningDescription)}");
                    }
                    isNotTimeout = replyHandleDict[msg.ID].WaitOne(IConfig.ReplyTimeout);
                    if (!isNotTimeout)
                    {
                        reTryTime--;
                    }
                }
            } while (reTryTime > 0 && !isNotTimeout);
            if (isWaitReply)
            {
                replyHandleDict[msg.ID].WaitOne(IConfig.ReplyTimeout);
                replyHandleDict.Remove(msg.ID);
            }
                if (replyHandleList.Contains(msg.ID))
            if (!isNotTimeout)
            {
                throw new ProcessException($"{Name}反馈数据超时\r\n" + msg.GetDisplayText());
            }
        }
        #region 执行结果属性
        public RobotPoint CurrentPoint { get; set; } = null;
        //public int SlotIndex { get; set; } = 0;
        #endregion
        public void Move(RobotPoint point, MoveType isAbs, bool isWaitFinished)
        {
            CurrentPoint = null;
            RobotMsg msg = new RobotMsg();
            msg.Type = RobotMsgType.Send;
            msg.ID = SID;
            msg.Action = RobotMsgAction.Move;
            msg.Para1 = (int)isAbs;
            msg.Point = point ?? new RobotPoint();
            SendMsg(msg, true);
            TaskTimeoutCheck(isWaitFinished, msg.ID, $"{Name}{(isAbs.GetEnumDescription())}至{point.GetDisplayText()}动作超时");
        }
        public void Load(RobotPoint point, TrayType trayType, bool isWaitFinished)
        {
            //SlotIndex = 0;
            RobotMsg msg = new RobotMsg();
            msg.Type = RobotMsgType.Send;
            msg.ID = SID;
            msg.Action = RobotMsgAction.Load;
            msg.Para1 = (int)trayType;
            msg.Point = point;
            SendMsg(msg, true);
            TaskTimeoutCheck(isWaitFinished, msg.ID, $"{Name}取{trayType.ToString()}{point.GetDisplayText()}动作超时");
            //if (isWaitFinished && SlotIndex <= 0)
            //{
            //    throw new ProcessException($"{Name}没有可用的空白格位,无法放置卷宗");
            //}
        }
        public void UnLoad(RobotPoint point, TrayType trayType, bool isWaitFinished)
        {
            RobotMsg msg = new RobotMsg();
            msg.Type = RobotMsgType.Send;
            msg.ID = SID;
            msg.Action = RobotMsgAction.Unload;
            msg.Para1 = (int)trayType;
            msg.Point = point;
            SendMsg(msg, true);
            TaskTimeoutCheck(isWaitFinished, msg.ID, $"{Name}卸{trayType.ToString()}{point.GetDisplayText()}动作超时");
        }
        public void GetPosition(bool isWaitFinished)
        {
            CurrentPoint = null;
            RobotMsg msg = new RobotMsg();
            msg.Type = RobotMsgType.Send;
            msg.ID = SID;
            msg.Action = RobotMsgAction.GetPosition;
            SendMsg(msg, true);
            TaskTimeoutCheck(isWaitFinished, msg.ID, $"{Name}获取当前坐标超时");
        }
        public void SetBasePoint()
        {
            RobotMsg msg = new RobotMsg();
            msg.Type = RobotMsgType.Send;
            msg.ID = SID;
            msg.Action = RobotMsgAction.BasePoint;
            SendMsg(msg, true);
        }
        public void SetIO(int outputIndex, bool isOn)
        {
            RobotMsg msg = new RobotMsg();
            msg.Type = RobotMsgType.Send;
            msg.ID = SID;
            msg.Action = RobotMsgAction.IOSet;
            msg.Para1 = outputIndex;
            msg.Para2 = isOn ? 1 : 0;
            SendMsg(msg, true);
        }
        private void TaskTimeoutCheck(bool isWaitFinished, int msgId, string errorMsg)
        {
            if (isWaitFinished)
            {
                taskHandleDict[msgId] = new ManualResetEvent(false);
                taskHandleDict[msgId].Reset();
                bool isNotTimeout = taskHandleDict[msgId].WaitOne((int)(IConfig.OperationTimeout * 60 * 1000));
                taskHandleDict.Remove(msgId);
                if (!isNotTimeout)
                {
                    throw new ProcessException("反馈数据超时\r\n" + msg.GetDisplayText(), null);
                    throw new ProcessException(errorMsg);
                }
            }
        }
        #region IMonitor
        public event OnMonitorInvokeDelegate OnMonitorInvoke;
        public event OnMonitorAlarmDelegate OnMonitorAlarm;
        protected List<int> oldValues = new List<int>();
        List<int> newValues = new List<int>();
        public ManualResetEvent MonitorHandle { get; set; } = new ManualResetEvent(false);
        public List<int> GetIOValues()
        {
            MonitorHandle.Reset();
            scanMsg.ID = SID;
            SendMsg(scanMsg, true, true);
            var isNotTimeout = MonitorHandle.WaitOne(IConfig.ReplyTimeout * 3);
            if (!isNotTimeout)
                throw new ProcessException($"{Name}监听操作超时");
            return newValues;
        }
        public virtual void Monitor()
        {
            while (CurrentState != EnumHelper.DeviceState.DSClose && CurrentState != EnumHelper.DeviceState.DSExcept)
            {
                try
                {
                    if (IConfig.IsEnableMonitor)
                    {
                        List<int> newValues = GetIOValues();
                        if (newValues == null || newValues.Count == 0)
                            continue;
                        if (oldValues.Count == 0)
                        {
                            oldValues = newValues.ConvertAll(s => -1).ToList();
                        }
                        if (oldValues.Count == newValues.Count)
                        {
                            var tempNew = new List<int>(newValues);
                            var tempOld = new List<int>(oldValues);
                            MonitorCheckAndInvoke(tempNew, tempOld);
                        }
                        oldValues = new List<int>(newValues);
                    }
                    Thread.Sleep(IConfig.ScanInterval);
                }
                catch (Exception ex)
                {
                    LogAsync(DateTime.Now, "监听异常", ex.GetExceptionMessage());
                }
            }
        }
        protected virtual void MonitorCheckAndInvoke(List<int> tempNew, List<int> tempOld)
        {
            IConfig.WarningSetCollection.ForEach(w =>
            {
                if (w.WarningIndex_Word < 0 || w.WarningIndex_Word >= tempNew.Count)
                    return;
                bool isOn = tempNew[w.WarningIndex_Word] == 1;
                if (w.CurrentStatus != isOn)
                {
                    w.CurrentStatus = isOn;
                    OnMonitorAlarm?.BeginInvoke(DateTime.Now, this, w, null, null);
                }
            });
            IConfig.MonitorSetCollection.ForEach(m =>
            {
                if (m.TriggerIndex < 0 || m.TriggerIndex >= tempNew.Count)
                {
                    return;
                }
                int newValue = tempNew[m.TriggerIndex];
                int oldValue = tempOld[m.TriggerIndex];
                if (newValue != oldValue)
                {
                    if (m.TriggerValue == -999 || newValue == m.TriggerValue)
                    {
                        if (m.OpConfig == null)
                        {
                            m.OpConfig = new OperationConfigBase();
                        }
                        m.OpConfig.InputPara = m.InputDataIndex.ConvertAll(index =>
                        {
                            return tempNew[index];
                        }).ToList();
                        OnMonitorInvoke?.BeginInvoke(DateTime.Now, this, m, null, null);
                    }
                }
            });
        }
        public virtual void ResetAlarm()
        {
            IConfig.WarningSetCollection.ForEach(u => u.CurrentStatus = !u.TriggerValue);
        }
        #endregion
    }
}