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