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;
}
}
}
}
}