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<DateTime, AuboRobotDriver, RobotMsg> 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<int>();
|
|
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<int, AutoResetEvent>();
|
replyErrorCodeDict = new Dictionary<int, int>();
|
taskHandleDict = new Dictionary<int, ManualResetEvent>();
|
}
|
|
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<string>());
|
|
//scanMsg = new RobotMsg();
|
//scanMsg.Action = RobotMsgAction.IO;
|
//scanMsg.Para1 = RobotMsgParas.Query;
|
|
//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<int>();
|
|
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<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 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.RobotWarnings.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<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.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);
|
}
|
}
|
});
|
}
|
#endregion
|
}
|
}
|