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; using System.Net; 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, IMonitor { public Action OnMsgReceived { get; set; } AuboRobotInitialConfig IConfig { get => InitialConfig as AuboRobotInitialConfig; } #region DeviceBase protected override void DeviceRun(IOperationConfig config) { } protected override void DeviceSet(IDeviceConfig config) { } protected override void Init() { oldValues = new List(); if (string.IsNullOrWhiteSpace(IConfig.EndChar)) { throw new ProcessException("协议文本的结束字符不可为空,请检查配置"); } client = new TcpClient(); client.SendBufferSize = client.ReceiveBufferSize = 0; client.Client.Blocking = true; client.NoDelay = true; client.BeginConnect(IPAddress.Parse(IConfig.RobotIP), IConfig.RobotPort, OnConnect, null); replyHandleDict = new Dictionary(); replyErrorCodeDict = new Dictionary(); taskHandleDict = new Dictionary(); } protected override void Pause() { } protected override void Resume() { } RobotMsg scanMsg = new RobotMsg(); protected override void Start() { //Query Robot IOs //SendMsg(RobotMsgType.Send, 0, true, RobotMsgAction.IO, RobotMsgParas.Query, new List()); scanMsg = new RobotMsg(); scanMsg.Action = RobotMsgAction.IOQuery; Task.Run(() => { Monitor(); }); } protected override void Stop() { 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) { try { client.EndConnect(ar); stream = client.GetStream(); stream.BeginRead(buffer, 0, buffer.Length, OnDataReceived, null); } catch (Exception ex) { //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 OnDataReceived(IAsyncResult ar) { try { int dataLength = stream.EndRead(ar); if (dataLength > 0) { 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 = ""; object analyzeLock = new object(); private async void AnalyzeData(string data) { await Task.Run(() => { lock (analyzeLock) { msg += data; var datas = msg.Split(new string[] { IConfig.EndChar }, StringSplitOptions.RemoveEmptyEntries); if (msg.EndsWith(IConfig.EndChar)) { msg = ""; } else { msg = datas[datas.Length - 1]; datas = datas.Take(datas.Length - 1).ToArray(); } AnalyzeDataList(datas); } }); } private async void AnalyzeDataList(string[] datas) { await Task.Run(() => { Array.ForEach(datas, data => { RobotMsg msg = RobotMsg.GetRobotMsg(data, IConfig.Seperator); 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(); 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); } } }); }); } int sid = 1; int SID { get { if (sid > 99) { sid = 1; } return sid++; } } Dictionary replyHandleDict = new Dictionary(); Dictionary replyErrorCodeDict = new Dictionary(); Dictionary taskHandleDict = new Dictionary(); public void SendReplyMsg(int replyId) { RobotMsg msg = new RobotMsg(); msg.Type = RobotMsgType.Rec; msg.ID = replyId; SendMsg(msg, false); } bool canMonitor = true; object monitorLock = new object(); public void SendMsg(RobotMsg msg, bool isWaitReply = true, bool isMonitorMsg = false) { replyErrorCodeDict[msg.ID] = 0; if (isWaitReply) { replyHandleDict[msg.ID] = new AutoResetEvent(false); } 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.Remove(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(errorMsg); } } } #region IMonitor public event OnMonitorInvokeDelegate OnMonitorInvoke; public event OnMonitorAlarmDelegate OnMonitorAlarm; protected List oldValues = new List(); List newValues = new List(); public ManualResetEvent MonitorHandle { get; set; } = new ManualResetEvent(false); public List 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 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(newValues); var tempOld = new List(oldValues); MonitorCheckAndInvoke(tempNew, tempOld); } oldValues = new List(newValues); } Thread.Sleep(IConfig.ScanInterval); } catch (Exception ex) { LogAsync(DateTime.Now, "监听异常", ex.GetExceptionMessage()); } } } protected virtual void MonitorCheckAndInvoke(List tempNew, List 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 } }