using HslCommunication.BasicFramework;
using HslCommunication.Core;
using HslCommunication.Serial;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace HslCommunication.Profinet.Keyence
{
///
/// 基恩士KV上位链路串口通信的对象,适用于Nano系列串口数据,以及L20V通信模块
///
///
/// 地址的输入的格式说明如下:
///
///
/// 地址名称
/// 地址代号
/// 示例
/// 地址进制
/// 字操作
/// 位操作
/// KV-7500/7300
/// KV-5500/5000/3000
/// KV Nano
///
/// -
/// 输入继电器
/// X
/// X100,X1A0
/// 16
/// √
/// √
/// R00000~R99915
/// R00000~R99915
/// R00000~R59915
///
/// -
/// 输出继电器
/// Y
/// Y100,Y1A0
/// 16
/// √
/// √
/// R00000~R99915
/// R00000~R99915
/// R00000~R59915
///
/// -
/// 内部辅助继电器
/// MR
/// MR100,M200
/// 10
/// √
/// √
/// MR00000~MR99915
/// MR00000~MR99915
/// MR00000~MR59915
///
/// -
/// 数据存储器
/// DM
/// DM100,DM200
/// 10
/// √
/// ×
/// DM00000~DM65534
/// DM00000~DM65534
/// DM00000~DM32767
///
/// -
/// 定时器(当前值)
/// TN
/// TN100,TN200
/// 10
/// √
/// ×
/// T0000~T3999
/// T0000~T3999
/// T000~T511
///
/// -
/// 定时器(接点)
/// TS
/// TS100,TS200
/// 10
/// √
/// √
/// T0000~T3999
/// T0000~T3999
/// T000~T511
///
/// -
/// 计数器(当前值)
/// CN
/// CN100,CN200
/// 10
/// √
/// ×
/// C0000~C3999
/// C0000~C3999
/// C000~C255
///
/// -
/// 计数器(接点)
/// CS
/// CS100,CS200
/// 10
/// √
/// √
/// C0000~C3999
/// C0000~C3999
/// C000~C255
///
///
///
public class KeyenceNanoSerial : SerialDeviceBase
{
#region Constructor
///
/// 实例化基恩士的串口协议的通讯对象
///
public KeyenceNanoSerial()
{
WordLength = 1;
}
///
/// 初始化后建立通讯连接
///
/// 是否初始化成功
protected override OperateResult InitializationOnOpen()
{
// 建立通讯连接{CR/r}
var result = ReadBase(_buildConnectCmd);
if (!result.IsSuccess) return result;
return OperateResult.CreateSuccessResult();
}
#endregion
#region Check Response
///
/// 校验读取返回数据状态
///
///
///
private OperateResult CheckPlcReadResponse(byte[] ack)
{
if (ack.Length == 0) return new OperateResult(StringResources.Language.MelsecFxReceiveZore);
if (ack[0] == 0x45) return new OperateResult(StringResources.Language.MelsecFxAckWrong + " Actual: " + SoftBasic.ByteToHexString(ack, ' '));
if ((ack[ack.Length - 1]!=0x0A) &&(ack[ack.Length -2]!=0x0D)) return new OperateResult(StringResources.Language.MelsecFxAckWrong + " Actual: " + SoftBasic.ByteToHexString(ack, ' '));
return OperateResult.CreateSuccessResult();
}
///
/// 校验写入返回数据状态
///
///
///
private OperateResult CheckPlcWriteResponse(byte[] ack)
{
if (ack.Length == 0) return new OperateResult(StringResources.Language.MelsecFxReceiveZore);
return OperateResult.CreateSuccessResult();
}
#endregion
#region Read Support
///
/// 建立读取指令
///
/// 软元件地址
/// 读取长度
/// 是否建立成功
private OperateResult BuildReadCommand(string address, ushort length)
{
var addressResult = KvCalculateWordStartAddress(address);
if (!addressResult.IsSuccess) return OperateResult.CreateFailedResult(addressResult);
ushort startAddress = addressResult.Content;
StringBuilder StrCommand = new StringBuilder();
StrCommand.Append("RDS"); //批量读取
StrCommand.Append(" "); //空格符
StrCommand.Append(address); //软元件地址,如DM100
StrCommand.Append(" "); //空格符
StrCommand.Append(length.ToString());
StrCommand.Append("\r"); //结束符
byte[] _PLCCommand = Encoding.ASCII.GetBytes(StrCommand.ToString().ToCharArray());
return OperateResult.CreateSuccessResult(_PLCCommand);
}
///
/// 读取设备的short类型的数据
///
/// 起始地址
/// 带成功标志的结果数据对象
public new OperateResult ReadInt16(string address)
{
var result= ReadInt16(address, 1);
if (!result.IsSuccess) return OperateResult.CreateFailedResult(result);
return OperateResult.CreateSuccessResult(result.Content[0]);
}
///
/// 读取设备的short类型的数组
///
/// 起始地址
/// 数组长度
/// 带成功标志的结果数据对象
public new OperateResult ReadInt16(string address, ushort length)
{
address += ".S";
return base.ReadInt16(address, length);
}
///
/// 读取设备的ushort数据类型的数据
///
/// 起始地址
/// 带成功标志的结果数据对象
public new OperateResult ReadUInt16(string address)
{
var result = ReadUInt16(address, 1);
if (!result.IsSuccess) return OperateResult.CreateFailedResult(result);
return OperateResult.CreateSuccessResult(result.Content[0]);
}
///
/// 读取设备的ushort类型的数组
///
/// 起始地址
/// 数组长度
/// 带成功标志的结果数据对象
public new OperateResult ReadUInt16(string address, ushort length)
{
address += ".U";
return base.ReadUInt16(address, length);
}
///
/// 读取设备的int类型的数据
///
/// 起始地址
/// 带成功标志的结果数据对象
public new OperateResult ReadInt32(string address)
{
var result = ReadInt32(address, 1);
if (!result.IsSuccess) return OperateResult.CreateFailedResult(result);
return OperateResult.CreateSuccessResult(result.Content[0]);
}
///
/// 读取设备的int类型的数组
///
/// 起始地址
/// 数组长度
/// 带成功标志的结果数据对象
public new OperateResult ReadInt32(string address, ushort length)
{
address += ".L";
return base.ReadInt32(address, length);
}
///
/// 读取设备的uint类型的数据
///
/// 起始地址
/// 带成功标志的结果数据对象
public new OperateResult ReadUInt32(string address)
{
var result = ReadUInt32(address, 1);
if (!result.IsSuccess) return OperateResult.CreateFailedResult(result);
return OperateResult.CreateSuccessResult(result.Content[0]);
}
///
/// 读取设备的uint类型的数组
///
/// 起始地址
/// 数组长度
/// 带成功标志的结果数据对象
public new OperateResult ReadUInt32(string address, ushort length)
{
address += ".D";
return base.ReadUInt32(address, length);
}
///
/// 从PLC中读取想要的数据,返回读取结果
///
public override OperateResult Read(string address, ushort length)
{
// 获取指令
OperateResult command = BuildReadCommand(address, length);
if (!command.IsSuccess) return OperateResult.CreateFailedResult(command);
// 核心交互
OperateResult read = ReadBase(command.Content);
if (!read.IsSuccess) return OperateResult.CreateFailedResult(read);
// 反馈检查
OperateResult ackResult = CheckPlcReadResponse(read.Content);
if (!ackResult.IsSuccess) return OperateResult.CreateFailedResult(ackResult);
// 数据提炼
return ExtractActualData(read.Content);
}
///
/// 成批读取Bool值
///
/// 地址信息
/// 数组长度
/// 带成功标志的结果数据对象
public OperateResult ReadBool(string address, ushort length)
{
var strBuffer = Encoding.Default.GetString(Read(address, length).Content).Split(' ');
var result = new bool[strBuffer.Length];
for (int i = 0; i < length; i++)
{
result[i] = strBuffer[i] == "1" ? true : false;
}
return OperateResult.CreateSuccessResult(result);
}
///
/// 读取单个Bool值
///
/// 地址信息
/// 带成功标志的结果数据对象
public OperateResult ReadBool(string address)
{
OperateResult read = ReadBool(address, 1);
if (!read.IsSuccess) return OperateResult.CreateFailedResult(read);
return OperateResult.CreateSuccessResult(read.Content[0]);
}
#endregion
#region Write Support
///
/// 写入转换后的数据值
///
/// 软元件地址
/// 转换后的Byte[]数据
/// 是否成功写入的结果
public override OperateResult Write(string address, byte[] value)
{
// 获取写入
OperateResult command = BuildWriteCommand(address, value);
if (!command.IsSuccess) return command;
// 核心交互
OperateResult read = ReadBase(command.Content);
if (!read.IsSuccess) return read;
// 结果验证
OperateResult checkResult = CheckPlcWriteResponse(read.Content);
if (!checkResult.IsSuccess) return checkResult;
return OperateResult.CreateSuccessResult();
}
///
/// 写入位数据的通断,支持的类型参考文档说明
///
/// 地址信息
/// 是否为通
/// 是否写入成功的结果对象
public OperateResult Write(string address, bool value)
{
//value=true时:指令尾部命令为" 1 1";value=false:指令尾部命令为" 1 0";
var byteTemp = value ? new byte[] {0x20, 0x31,0x20,0x31 } : new byte[] { 0x20, 0x31, 0x20, 0x30 };
// 先获取指令
OperateResult command = BuildWriteCommand(address, byteTemp);
if (!command.IsSuccess) return command;
// 和串口进行核心的数据交互
OperateResult read = ReadBase(command.Content);
if (!read.IsSuccess) return read;
// 检查结果是否正确
OperateResult checkResult = CheckPlcWriteResponse(read.Content);
if (!checkResult.IsSuccess) return checkResult;
return OperateResult.CreateSuccessResult();
}
///
/// 建立写入指令
///
/// 软元件地址
/// 转换后的数据
///
private OperateResult BuildWriteCommand(string address, byte[] value)
{
var addressResult = KvCalculateWordStartAddress(address);
if (!addressResult.IsSuccess) return OperateResult.CreateFailedResult(addressResult);
ushort startAddress = addressResult.Content;
StringBuilder StrCommandHead = new StringBuilder();
StrCommandHead.Append("WRS"); //批量读取
StrCommandHead.Append(" "); //空格符
StrCommandHead.Append(address); //软元件地址,如DM100;可加格式后缀,如DM100.S(表示有符号16位十进制)
byte[] _CommandHead = Encoding.ASCII.GetBytes(StrCommandHead.ToString().ToCharArray());
byte[] result = new byte[value.Length + _CommandHead.Length + 1];
Array.Copy(_CommandHead, result, _CommandHead.Length);
Array.Copy(value, 0, result, _CommandHead.Length, value.Length);
result[result.Length - 1] = 0x0d;
return OperateResult.CreateSuccessResult(result); // Return
}
#endregion
#region Private Member
private byte[] _buildConnectCmd = new byte[3] { 0x43, 0x52, 0x0d }; // 建立通讯连接{CR/r}
private byte[] _writeOkReturn = new byte[] { 0x4f, 0x4b, 0x0d, 0x0a }; // 写入数据成功返回指令
#endregion
#region Object Override
///
/// 返回表示当前对象的字符串
///
/// 字符串信息
public override string ToString( )
{
return $"KeyenceNanoSerial[{PortName}:{BaudRate}]";
}
#endregion
#region Static Method Helper
///
/// 从PLC反馈的数据进行提炼操作
///
/// PLC反馈的真实数据
/// 数据提炼后的真实数据
public static OperateResult ExtractActualData(byte[] response)
{
try
{
string strResponse = Encoding.Default.GetString(response);
byte[] data = new byte[response.Length - 2];
Array.Copy(response, data, response.Length - 2);
return OperateResult.CreateSuccessResult(data);
}
catch (Exception ex)
{
return new OperateResult()
{
Message = "Extract Msg:" + ex.Message + Environment.NewLine +
"Data: " + BasicFramework.SoftBasic.ByteToHexString(response)
};
}
}
///
/// 返回读取的地址及长度信息
///
/// 读取的地址信息
/// 带起始地址的结果对象
private static OperateResult KvCalculateWordStartAddress(string address)
{
// 初步解析,失败就返回
var analysis = KvAnalysisAddress(address);
if (!analysis.IsSuccess) return OperateResult.CreateFailedResult(analysis);
// 二次解析
ushort startAddress = analysis.Content2;
return OperateResult.CreateSuccessResult(startAddress);
}
///
/// 解析数据地址成不同的Keyence地址类型
///
/// 数据地址
/// 地址结果对象
private static OperateResult KvAnalysisAddress(string address)
{
var result = new OperateResult();
try
{
//删除软元件后缀,如DM100.L—>DM100
if (address.Contains("."))
{
address = address.Substring(0, address.Length - 2);
}
switch (address[0])
{
case 'M':
case 'm':
{
result.Content1 = KeyenceDataType.M;
if (address[1] == 'R' || address[1] == 'r')
{
result.Content2 = Convert.ToUInt16(address.Substring(2), KeyenceDataType.M.FromBase);
}
else
{
result.Content2 = Convert.ToUInt16(address.Substring(1), KeyenceDataType.M.FromBase);
}
break;
}
case 'R':
case 'r':
{
result.Content1 = KeyenceDataType.R;
result.Content2 = Convert.ToUInt16(address.Substring(1), KeyenceDataType.R.FromBase);
break;
}
case 'X':
case 'x':
{
result.Content1 = KeyenceDataType.X;
result.Content2 = Convert.ToUInt16(address.Substring(1), 8);
break;
}
case 'Y':
case 'y':
{
result.Content1 = KeyenceDataType.Y;
result.Content2 = Convert.ToUInt16(address.Substring(1), 8);
break;
}
case 'D':
case 'd':
{
result.Content1 = KeyenceDataType.D;
if (address[1] == 'M' || address[1] == 'm')
{
result.Content2 = Convert.ToUInt16(address.Substring(2), KeyenceDataType.D.FromBase);
}
else
{
result.Content2 = Convert.ToUInt16(address.Substring(1), KeyenceDataType.D.FromBase);
}
break;
}
case 'T':
case 't':
{
if (address[1] == 'N' || address[1] == 'n')
{
result.Content1 = KeyenceDataType.TN;
result.Content2 = Convert.ToUInt16(address.Substring(2), KeyenceDataType.TN.FromBase);
break;
}
else if (address[1] == 'S' || address[1] == 's')
{
result.Content1 = KeyenceDataType.TS;
result.Content2 = Convert.ToUInt16(address.Substring(2), KeyenceDataType.TS.FromBase);
break;
}
else
{
throw new Exception(StringResources.Language.NotSupportedDataType);
}
}
case 'C':
case 'c':
{
if (address[1] == 'N' || address[1] == 'n')
{
result.Content1 = KeyenceDataType.CN;
result.Content2 = Convert.ToUInt16(address.Substring(2), KeyenceDataType.CN.FromBase);
break;
}
else if (address[1] == 'S' || address[1] == 's')
{
result.Content1 = KeyenceDataType.CS;
result.Content2 = Convert.ToUInt16(address.Substring(2), KeyenceDataType.CS.FromBase);
break;
}
else
{
throw new Exception(StringResources.Language.NotSupportedDataType);
}
}
default: throw new Exception(StringResources.Language.NotSupportedDataType);
}
}
catch (Exception ex)
{
result.Message = ex.Message;
return result;
}
result.IsSuccess = true;
return result;
}
#endregion
}
}