123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720 |
- 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 常量
- /// <summary>
- /// 开始标识
- /// </summary>
- private const byte START_FLAG = 0X01;
- /// <summary>
- /// 结束标识
- /// </summary>
- private const byte END_FLAG = 0x04;
- /// <summary>
- /// 最小长度
- /// </summary>
- private const byte MIN_LENGTH = 5;
- /// <summary>
- /// 最大长度(1(Header)+1(ID)+1(Length)+3(StartData+Msg High+Msg Low)+n(Data最大63)+1(End))
- /// </summary>
- private const byte MAX_LENGTH = 70;
- /// <summary>
- /// 缓存最大
- /// </summary>
- private const int BUFFER_LENGTH = 65535;
- #endregion
- #region 内部变量
- /// <summary>
- /// 串口
- /// </summary>
- private SerialPort _serialPort;
- /// <summary>
- /// 缓存
- /// </summary>
- private byte[] _buffer = new byte[BUFFER_LENGTH];
- /// <summary>
- /// 缓存锁
- /// </summary>
- private object _lock = new object();
- /// <summary>
- /// 偏移量
- /// </summary>
- private int offset = 0;
- /// <summary>
- /// 数量
- /// </summary>
- private int count = 0;
- /// <summary>
- /// 定时任务
- /// </summary>
- private PeriodicJob _periodJob;
- /// <summary>
- /// 发送定时任务
- /// </summary>
- private PeriodicJob _sendPeriodJob;
- /// <summary>
- /// 连接状态
- /// </summary>
- private bool _connected;
- /// <summary>
- /// 发送队列
- /// </summary>
- private ConcurrentQueue<byte[]> _sendOperationQueue = new ConcurrentQueue<byte[]>();
- /// <summary>
- /// 模块名称
- /// </summary>
- private string _name;
- /// <summary>
- /// 支持重连
- /// </summary>
- private bool _reconnect;
- /// <summary>
- /// 错误
- /// </summary>
- private string _errmsg;
- /// <summary>
- /// 离线时间
- /// </summary>
- private DateTime _offlineDateTime = DateTime.Now;
- /// <summary>
- /// 首次连接成功
- /// </summary>
- private bool _isFirstConnected = false;
- /// <summary>
- /// 自增锁
- /// </summary>
- private object _autoCountLock = new object();
- /// <summary>
- /// 自动增加计数
- /// </summary>
- private byte _autoCount = 0;
- #endregion
- #region 属性
- /// <summary>
- /// 连接状态
- /// </summary>
- public bool Connected
- {
- get { return _connected; }
- set { _connected = value; }
- }
- #endregion
- /// <summary>
- /// 初始化
- /// </summary>
- /// <param name="portName"></param>
- /// <param name="baudRate"></param>
- /// <param name="stopBits"></param>
- /// <param name="dataBits"></param>
- /// <param name="parity"></param>
- 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);
- }
- /// <summary>
- /// 初始化
- /// </summary>
- public void Initialize()
- {
- _periodJob.Start();
- _sendPeriodJob.Start();
- }
- /// <summary>
- /// 启动
- /// </summary>
- 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;
- }
- }
- }
- /// <summary>
- /// 关闭
- /// </summary>
- public void Close()
- {
- try
- {
- _connected = false;
- _serialPort.Close();
- _periodJob.Stop();
- }
- catch (Exception ex)
- {
- WriteErrorMsg(ex.Message);
- }
- }
- /// <summary>
- /// 定时器
- /// </summary>
- /// <returns></returns>
- 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;
- }
- /// <summary>
- /// 发送定时器
- /// </summary>
- /// <returns></returns>
- 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;
- }
- /// <summary>
- /// 清空发送队列
- /// </summary>
- private void ClearSendQueue()
- {
- try
- {
- while(_sendOperationQueue.Count!=0)
- {
- _sendOperationQueue.TryDequeue(out var result);
- }
- }
- catch
- {
- }
- }
- /// <summary>
- /// 校验
- /// </summary>
- /// <returns></returns>
- private (int, int, bool) CheckValid()
- {
- int startIndex = -1;
- int endIndex = -1;
- bool result = false;
- if(count<MIN_LENGTH)
- {
- return (-1, -1, false);
- }
- int dataLength = 0;
- for (int i = 0; i < count; i++)
- {
- if (_buffer[i] == START_FLAG && startIndex == -1)
- {
- startIndex = i;
- dataLength = _buffer[2];
- }
- else if (_buffer[i] == END_FLAG)
- {
- if (i == dataLength + 3)
- {
- endIndex = i;
- break;
- }
- }
- }
- if (endIndex > startIndex)
- {
- result = true;
- }
- return (startIndex, endIndex, result);
- }
- /// <summary>
- /// 解析标准包
- /// </summary>
- /// <param name="byt"></param>
- 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);
- }
- }
- /// <summary>
- /// 出现错误
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
- {
- LOG.WriteLog(eEvent.ERR_LINMOT, _name, e.EventType.ToString());
- }
- /// <summary>
- /// 串口接收
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- 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;
- }
- }
- /// <summary>
- /// 发送指令
- /// </summary>
- /// <param name="id"></param>
- /// <param name="operation"></param>
- 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;
- }
- }
- /// <summary>
- /// 发送Curve指令
- /// </summary>
- /// <param name="id"></param>
- /// <param name="curveCount"></param>
- /// <param name="curveId"></param>
- /// <param name="timeScale"></param>
- /// <param name="curveOffset"></param>
- /// <param name="amplitudeScale"></param>
- 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;
- }
- }
- /// <summary>
- /// 发送CommandTable
- /// </summary>
- /// <param name="id"></param>
- /// <param name="curveCount"></param>
- /// <param name="entryId"></param>
- /// <returns></returns>
- 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;
- }
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="id"></param>
- /// <param name="lowByte"></param>
- /// <param name="highByte"></param>
- /// <param name="intValue"></param>
- /// <returns></returns>
- 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;
- }
- }
- /// <summary>
- /// 发送GAI GoToPosition指令
- /// </summary>
- /// <param name="id"></param>
- /// <param name="autoCount"></param>
- /// <param name="curveId"></param>
- /// <param name="timeScale"></param>
- /// <param name="curveOffset"></param>
- /// <param name="amplitudeScale"></param>
- 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;
- }
- }
- /// <summary>
- /// 发送GAI GoToPosition After Actual Position指令
- /// </summary>
- /// <param name="id"></param>
- /// <param name="autoCount"></param>
- /// <param name="curveId"></param>
- /// <param name="timeScale"></param>
- /// <param name="curveOffset"></param>
- /// <param name="amplitudeScale"></param>
- 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;
- }
- }
- /// <summary>
- /// 创建操作指令
- /// </summary>
- /// <param name="id"></param>
- /// <param name="operation"></param>
- /// <returns></returns>
- 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 };
- }
- }
- /// <summary>
- /// 记录错误信息
- /// </summary>
- /// <param name="msg"></param>
- 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);
- }
- }
- /// <summary>
- /// 写日志
- /// </summary>
- /// <param name="bytes"></param>
- private void WriteInfoMsg(int logType,byte[] bytes)
- {
- bool enableLog = false;
- if (SC.ContainsItem("Log.EnableLinmotLog"))
- {
- enableLog = SC.GetValue<bool>("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}");
- }
- }
- /// <summary>
- /// 写日志
- /// </summary>
- /// <param name="bytes"></param>
- private void WriteInfoMsg(int logType, string str)
- {
- bool enableLog = false;
- if (SC.ContainsItem("Log.EnableLinmotLog"))
- {
- enableLog = SC.GetValue<bool>("Log.EnableLinmotLog");
- }
- if (enableLog)
- {
- string type = logType == 0 ? "receive" : "send";
- LOG.WriteBackgroundLog(eEvent.INFO_LINMOT, _name, $"{type} {str}");
- }
- }
- /// <summary>
- /// Curve每次数值得不同
- /// </summary>
- private void GenerateAutoCount()
- {
- lock (_autoCountLock)
- {
- _autoCount++;
- if (_autoCount >= 0x0D)
- {
- _autoCount = 1;
- }
- }
- }
- }
- }
|