using Aitex.Core.RT.Log; using Aitex.Core.RT.OperationCenter; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using DocumentFormat.OpenXml.ExtendedProperties; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace MECF.Framework.Common.Device.LinMot { public enum LinMotOperation { None = 0, SwitchOn = 1, SwitchOff = 2, Home = 3, Curve = 4, GoToInitialPosition = 5, CurveGotoInitialPosition = 6, Reset=7, ClearError=8, StartCurve=9, StopMotor=10, ControlWordGoToPosition=11, Freeze=12, StartVAIGoToPosition=13, ReadStatus=99 } public class LinMotSerialDevice { #region 常量 /// /// 开始标识 /// private const byte START_FLAG = 0X01; /// /// 结束标识 /// private const byte END_FLAG = 0x04; /// /// 最小长度 /// private const byte MIN_LENGTH = 5; /// /// 最大长度(1(Header)+1(ID)+1(Length)+3(StartData+Msg High+Msg Low)+n(Data最大63)+1(End)) /// private const byte MAX_LENGTH = 70; /// /// 缓存最大 /// private const int BUFFER_LENGTH = 65535; #endregion #region 内部变量 /// /// 串口 /// private SerialPort _serialPort; /// /// 缓存 /// private byte[] _buffer = new byte[BUFFER_LENGTH]; /// /// 缓存锁 /// private object _lock = new object(); /// /// 偏移量 /// private int offset = 0; /// /// 数量 /// private int count = 0; /// /// 定时任务 /// private PeriodicJob _periodJob; /// /// 发送定时任务 /// private PeriodicJob _sendPeriodJob; /// /// 连接状态 /// private bool _connected; /// /// 发送队列 /// private ConcurrentQueue _sendOperationQueue = new ConcurrentQueue(); /// /// 模块名称 /// private string _name; /// /// 支持重连 /// private bool _reconnect; /// /// 错误 /// private string _errmsg; /// /// 离线时间 /// private DateTime _offlineDateTime = DateTime.Now; /// /// 首次连接成功 /// private bool _isFirstConnected = false; /// /// 自增锁 /// private object _autoCountLock = new object(); /// /// 自动增加计数 /// private byte _autoCount = 0; #endregion #region 属性 /// /// 连接状态 /// public bool Connected { get { return _connected; } set { _connected = value; } } #endregion /// /// 初始化 /// /// /// /// /// /// public LinMotSerialDevice(string name, string portName, int baudRate = 9600, StopBits stopBits = StopBits.One, int dataBits = 8, Parity parity = Parity.None, bool reconnect = false) { _serialPort = new SerialPort(); _serialPort.BaudRate = baudRate; _serialPort.StopBits = stopBits; _serialPort.DataBits = dataBits; _serialPort.Parity = parity; _serialPort.PortName = portName; _serialPort.DataReceived += SerialPort_DataReceived; _serialPort.ErrorReceived += SerialPort_ErrorReceived; _name = name; _reconnect = reconnect; _periodJob = new PeriodicJob(50, OnTimer, $"{_name}_Analyser", false, true); _sendPeriodJob = new PeriodicJob(50, OnSenderTimer, $"{_name}_Sender", false, true); } /// /// 初始化 /// public void Initialize() { _periodJob.Start(); _sendPeriodJob.Start(); } /// /// 启动 /// public void Start() { if (!_connected) { try { _serialPort.Open(); LOG.WriteLog(eEvent.INFO_LINMOT, _name, $"connect port[{_serialPort.PortName}] success"); _connected = true; } catch (Exception ex) { WriteErrorMsg(ex.Message); } if (!_isFirstConnected) { _isFirstConnected = true; } } } /// /// 关闭 /// public void Close() { try { _connected = false; _serialPort.Close(); _periodJob.Stop(); } catch (Exception ex) { WriteErrorMsg(ex.Message); } } /// /// 定时器 /// /// private bool OnTimer() { lock (_lock) { if (count < MIN_LENGTH) { return true; } } try { var result = CheckValid(); if (result.Item3) { lock (_lock) { int startIndex = result.Item1; int endIndex = result.Item2; byte[] byt = new byte[endIndex - startIndex + 1]; Array.Copy(_buffer, startIndex, byt, 0, byt.Length); Array.Copy(_buffer, endIndex + 1, _buffer, 0, count - endIndex - 1); count = count - endIndex - 1; offset = count; _buffer[count] = 0; _buffer[count + 1] = 0; _buffer[count + 2] = 0; _buffer[count + 3] = 0; Analyse(byt); } } else { lock (_lock) { int startIndex = result.Item1; int endIndex = result.Item2; if (startIndex == -1) { _buffer = new byte[BUFFER_LENGTH]; offset = 0; count = 0; } else if (startIndex > 0) { Array.Copy(_buffer, startIndex, _buffer, 0, count - startIndex); count = count - startIndex; offset = count; } else if (endIndex == -1 && count > MAX_LENGTH)//一直找不到结束标识,长度超过最大长度 { //仅清除第一个StartFlag Array.Copy(_buffer, 1, _buffer, 0, count - 1); count = count - 1; offset = count; } } } } catch (Exception ex) { LOG.WriteLog(eEvent.ERR_LINMOT, _name, ex.StackTrace); } return true; } /// /// 发送定时器 /// /// private bool OnSenderTimer() { if(!_isFirstConnected) { return true; } if (!_connected) { if (_sendOperationQueue.Count != 0) { ClearSendQueue(); } if (_reconnect) { Start(); } else { _periodJob.Stop(); } return true; } else { int queueCount = _sendOperationQueue.Count; if (_sendOperationQueue.Count != 0) { WriteInfoMsg(1, $"start send buffer queue length {queueCount}"); if (_sendOperationQueue.TryDequeue(out byte[] sendBuffer)) { try { WriteInfoMsg(1, $"start send buffer get queue data"); _serialPort.Write(sendBuffer, 0, sendBuffer.Length); WriteInfoMsg(1, sendBuffer); WriteInfoMsg(1, $"send buffer queue length {queueCount}"); } catch (Exception ex) { WriteErrorMsg(ex.Message); } } } } return true; } /// /// 清空发送队列 /// private void ClearSendQueue() { try { while(_sendOperationQueue.Count!=0) { _sendOperationQueue.TryDequeue(out var result); } } catch { } } /// /// 校验 /// /// private (int, int, bool) CheckValid() { int startIndex = -1; int endIndex = -1; bool result = false; if(count startIndex) { result = true; } return (startIndex, endIndex, result); } /// /// 解析标准包 /// /// private void Analyse(byte[] byt) { if (byt.Length < 19) { return; } byte id = byt[1];//Id short statusWord = BitConverter.ToInt16(byt,7);//7状态字高位 8状态字低位 short errorCode=BitConverter.ToInt16(byt,11);//11错误码高位 12错误码低位 int currentPosition = BitConverter.ToInt32(byt, 15); string _linmotDeviceModuleName = LinMotDeviceConfigManager.Instance.GetModuleNameByAddress(_name, id); if (!string.IsNullOrEmpty(_linmotDeviceModuleName)) { LinMotDeviceConfigManager.Instance.UpdateModuleData(_linmotDeviceModuleName, statusWord, errorCode, currentPosition); } } /// /// 出现错误 /// /// /// private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e) { LOG.WriteLog(eEvent.ERR_LINMOT, _name, e.EventType.ToString()); } /// /// 串口接收 /// /// /// private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { byte[] readBuffer = new byte[_serialPort.BytesToRead]; int readCount = _serialPort.Read(readBuffer, 0, readBuffer.Length); if (readCount == 0) { return; } WriteInfoMsg(0,readBuffer); lock (_lock) { Buffer.BlockCopy(readBuffer, 0, _buffer, offset, readCount); offset = offset + readCount; count = offset; } } /// /// 发送指令 /// /// /// public bool SendOperation(byte id,LinMotOperation operation) { if (_connected) { byte[] bytes = GenerateOperationBytes(id, operation); _sendOperationQueue.Enqueue(bytes); return true; } else { WriteErrorMsg($"{_name} is not connected"); return false; } } /// /// 发送Curve指令 /// /// /// /// /// /// /// public bool SendCurveOperation(byte id,ushort curveId,ushort timeScale,int curveOffset=0,short amplitudeScale=100) { if (_connected) { byte[] curveIdByt = BitConverter.GetBytes(curveId); byte[] curveOffsetByte = BitConverter.GetBytes(curveOffset); //timeScal--unit 0.01% byte[] timeScaleByt = BitConverter.GetBytes((ushort)(timeScale * 100)); //amplitudeScale--unit 0.1% byte[] amplitudeScaleByte = BitConverter.GetBytes((short)(amplitudeScale * 10)); GenerateAutoCount(); byte[] byt = new byte[] { 0x01, id, 0x0F, 0x02, 0x00, 0x02, (byte)(0x40 | _autoCount), 0x04, curveIdByt[0], curveIdByt[1],curveOffsetByte[0], curveOffsetByte[1], curveOffsetByte[2], curveOffsetByte[3], timeScaleByt[0], timeScaleByt[1], amplitudeScaleByte[0], amplitudeScaleByte[1], 0x04 }; _sendOperationQueue.Enqueue(byt); string str = string.Join(" ", Array.ConvertAll(byt, x => x.ToString("X2"))); LOG.WriteBackgroundLog(eEvent.INFO_LINMOT, _name, $"curve operation {str}"); return true; } else { WriteErrorMsg($"{_name} is not connected"); return false; } } /// /// 发送CommandTable /// /// /// /// /// public bool SendCommandTableOpertaion(byte id, short entryId=0) { if (_connected) { byte[] entryIdByt = BitConverter.GetBytes(entryId); GenerateAutoCount(); byte[] byt = new byte[] { 0x01, id, 0x07, 0x02, 0x00, 0x02, (byte)(0x00 | _autoCount), 0x20, entryIdByt[0], entryIdByt[1], 0x04 }; _sendOperationQueue.Enqueue(byt); string str = string.Join(" ", Array.ConvertAll(byt, x => x.ToString("X2"))); LOG.WriteBackgroundLog(eEvent.INFO_LINMOT, _name, $"command table {str}"); return true; } else { WriteErrorMsg($"{_name} is not connected"); return false; } } /// /// /// /// /// /// /// /// public bool SendWriteRamIntValue(byte id,byte lowByte,byte highByte,int intValue) { if (_connected) { byte[] curveOffsetByte = BitConverter.GetBytes(intValue); byte[] byt = new byte[] { 0x01, id, 0x09, 0x02, 0x01, 0x03, lowByte, highByte, curveOffsetByte[0], curveOffsetByte[1], curveOffsetByte[2], curveOffsetByte[3],0x04 }; _sendOperationQueue.Enqueue(byt); string str = string.Join(" ", Array.ConvertAll(byt, x => x.ToString("X2"))); LOG.WriteBackgroundLog(eEvent.INFO_LINMOT, _name, $"Write Ram Int {str}"); return true; } else { WriteErrorMsg($"{_name} is not connected"); return false; } } /// /// 发送GAI GoToPosition指令 /// /// /// /// /// /// /// public bool SendVAIGoToPositionOpertaion(byte id, int position,int velocity,int accel,int decel) { if (_connected) { //int positionValue = (int)Math.Round(position * 10000, 0); byte[] positionByt = BitConverter.GetBytes(position); //unit 0.1μm 1mm=1000μm //int velocityValue = (int)Math.Round(velocity * 1000); byte[] velocityByt = BitConverter.GetBytes(velocity); //accel--unit 0.1m/s^2 //byte[] accelByt = BitConverter.GetBytes(accel * 1000); byte[] accelByt = BitConverter.GetBytes(accel); //decel--unit 0.1m/s^2 //byte[] decelByte = BitConverter.GetBytes(decel * 1000); byte[] decelByte = BitConverter.GetBytes(decel); GenerateAutoCount(); byte[] byt = new byte[] { 0x01, id, 0x15, 0x02, 0x00, 0x02, (byte)(0x00 | _autoCount), 0x01, positionByt[0], positionByt[1], positionByt[2], positionByt[3], velocityByt[0],velocityByt[1],velocityByt[2],velocityByt[3],accelByt[0],accelByt[1],accelByt[2],accelByt[3], decelByte[0],decelByte[1],decelByte[2],decelByte[3],0x04 }; _sendOperationQueue.Enqueue(byt); return true; } else { WriteErrorMsg($"{_name} is not connected"); return false; } } /// /// 发送GAI GoToPosition After Actual Position指令 /// /// /// /// /// /// /// public bool SendVAIGoToPositionAfterAutalCommandOpertaion(byte id, int position, int velocity, int accel, int decel) { if (_connected) { //int positionValue = (int)Math.Round(position * 10000, 0); byte[] positionByt = BitConverter.GetBytes(position); //unit 0.1μm 1mm=1000μm //int velocityValue = (int)Math.Round(velocity * 1000); byte[] velocityByt = BitConverter.GetBytes(velocity); //accel--unit 0.1m/s^2 //byte[] accelByt = BitConverter.GetBytes(accel * 1000); byte[] accelByt = BitConverter.GetBytes(accel); //decel--unit 0.1m/s^2 //byte[] decelByte = BitConverter.GetBytes(decel * 1000); byte[] decelByte = BitConverter.GetBytes(decel); GenerateAutoCount(); byte[] byt = new byte[] { 0x01, id, 0x15, 0x02, 0x00, 0x02, (byte)(0x80 | _autoCount), 0x01, positionByt[0], positionByt[1], positionByt[2], positionByt[3], velocityByt[0],velocityByt[1],velocityByt[2],velocityByt[3],accelByt[0],accelByt[1],accelByt[2],accelByt[3], decelByte[0],decelByte[1],decelByte[2],decelByte[3],0x04 }; _sendOperationQueue.Enqueue(byt); return true; } else { WriteErrorMsg($"{_name} is not connected"); return false; } } /// /// 创建操作指令 /// /// /// /// private byte[] GenerateOperationBytes(byte id,LinMotOperation operation) { switch(operation) { case LinMotOperation.ReadStatus: return new byte[] { 0x01, id, 0x03, 0x02, 0x01, 0x00, 0x04 }; case LinMotOperation.SwitchOn: return new byte[] { 0x01, id, 0x05, 0x02, 0x00, 0x01, 0x3F, 0x00, 0x04 }; case LinMotOperation.SwitchOff: return new byte[] { 0x01, id, 0x05, 0x02, 0x00, 0x01, 0x3E, 0x00, 0x04 }; case LinMotOperation.Home: return new byte[] { 0x01, id, 0x05, 0x02, 0x00, 0x01, 0x3F, 0x08, 0x04 }; case LinMotOperation.ClearError: return new byte[] { 0x01, id, 0x05, 0x02, 0x00, 0x01, 0xBF, 0x00, 0x04 }; case LinMotOperation.GoToInitialPosition: return new byte[]{ 0x01, id, 0x05, 0x02, 0x00, 0x01, 0x3F, 0x20, 0x04 }; case LinMotOperation.ControlWordGoToPosition: return new byte[] { 0x01, id, 0x05, 0x02, 0x00, 0x01, 0x7F, 0x00, 0x04 }; case LinMotOperation.Freeze: return new byte[] { 0x01, id, 0x05, 0x02, 0x00, 0x01, 0x1F, 0x00, 0x04 }; default: return new byte[] { 0x01, id, 0x03, 0x02, 0x01, 0x00, 0x04 }; } } /// /// 记录错误信息 /// /// private void WriteErrorMsg(string msg, bool disConnected = true) { if (disConnected) { _connected = false; _offlineDateTime = DateTime.Now; } if (_errmsg != msg) { _errmsg = msg; LOG.WriteLog(eEvent.ERR_LINMOT, _name, msg); } } /// /// 写日志 /// /// private void WriteInfoMsg(int logType,byte[] bytes) { bool enableLog = false; if (SC.ContainsItem("Log.EnableLinmotLog")) { enableLog = SC.GetValue("Log.EnableLinmotLog"); } if (enableLog) { string str = string.Join(" ", Array.ConvertAll(bytes, x => x.ToString("X2"))); string type = logType == 0 ? "receive" : "send"; LOG.WriteBackgroundLog(eEvent.INFO_LINMOT, _name, $"{type} {str}"); } } /// /// 写日志 /// /// private void WriteInfoMsg(int logType, string str) { bool enableLog = false; if (SC.ContainsItem("Log.EnableLinmotLog")) { enableLog = SC.GetValue("Log.EnableLinmotLog"); } if (enableLog) { string type = logType == 0 ? "receive" : "send"; LOG.WriteBackgroundLog(eEvent.INFO_LINMOT, _name, $"{type} {str}"); } } /// /// Curve每次数值得不同 /// private void GenerateAutoCount() { lock (_autoCountLock) { _autoCount++; if (_autoCount >= 0x0D) { _autoCount = 1; } } } } }