using MECF.Framework.RT.Core.IoProviders.Common; using MECF.Framework.RT.Core.IoProviders.Common.Serial; using MECF.Framework.RT.Core.IoProviders.Common.Transfer; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace MECF.Framework.RT.Core.IoProviders.Omron { /// /// 欧姆龙的HostLink协议的实现 /// /// /// 感谢 深圳~拾忆 的测试 /// 欧姆龙的地址参考如下: /// 地址支持的列表如下: /// /// /// 地址名称 /// 地址代号 /// 示例 /// 地址进制 /// 字操作 /// 位操作 /// 备注 /// /// /// DM Area /// D /// D100,D200 /// 10 /// /// /// /// /// /// CIO Area /// C /// C100,C200 /// 10 /// /// /// /// /// /// Work Area /// W /// W100,W200 /// 10 /// /// /// /// /// /// Holding Bit Area /// H /// H100,H200 /// 10 /// /// /// /// /// /// Auxiliary Bit Area /// A /// A100,A200 /// 10 /// /// /// /// /// /// public class OmronHostLink : SerialDeviceBase { #region Constructor /// /// 实例化一个默认的对象 /// public OmronHostLink() { this.WordLength = 1; this.ByteTransform.DataFormat = DataFormat.CDAB; } #endregion #region Public Member /// /// Specifies whether or not there are network relays. Set “80” (ASCII: 38,30) /// when sending an FINS command to a CPU Unit on a network.Set “00” (ASCII: 30,30) /// when sending to a CPU Unit connected directly to the host computer. /// public byte ICF { get; set; } = 0x00; /// /// PLC的单元号地址 /// /// /// 通常都为0 /// public byte DA2 { get; set; } = 0x00; /// /// 上位机的单元号地址 /// public byte SA2 { get; set; } /// /// 设备的标识号 /// public byte SID { get; set; } = 0x00; /// /// The response wait time sets the time from when the CPU Unit receives a command block until it starts /// to return a response.It can be set from 0 to F in hexadecimal, in units of 10 ms. /// /// /// If F(15) is set, the response will begin to be returned 150 ms (15 × 10 ms) after the command block was received. /// public byte ResponseWaitTime { get; set; } = 0x30; /// /// PLC设备的站号信息 /// public byte UnitNumber { get; set; } #endregion #region Read Write Support /// /// 批量读取PLC的数据,以字为单位,具体的地址参考文档 /// /// 地址信息 /// 数据长度 /// 读取结果信息 public override OperateResult Read(string address, ushort length) { // 解析地址 var command = OmronFinsNetHelper.BuildReadCommand(address, length, false); if (!command.IsSuccess) return command; // 核心交互 OperateResult read = ReadBase(PackCommand(command.Content)); if (!read.IsSuccess) return OperateResult.CreateFailedResult(read); // 数据有效性分析 OperateResult valid = ResponseValidAnalysis(read.Content, true); if (!valid.IsSuccess) return OperateResult.CreateFailedResult(valid); // 读取到了正确的数据 return OperateResult.CreateSuccessResult(valid.Content); } /// /// 批量写入PLC的数据,以字为单位,也就是说最少2个字节信息,具体的地址参考文档 /// /// 地址信息 /// 数据值 /// 是否写入成功 public override OperateResult Write(string address, byte[] value) { // 获取指令 var command = OmronFinsNetHelper.BuildWriteWordCommand(address, value, false); ; if (!command.IsSuccess) return command; // 核心数据交互 OperateResult read = ReadBase(PackCommand(command.Content)); if (!read.IsSuccess) return read; // 数据有效性分析 OperateResult valid = ResponseValidAnalysis(read.Content, false); if (!valid.IsSuccess) return valid; // 成功 return OperateResult.CreateSuccessResult(); } #endregion #region Bool Read Write /// /// 从欧姆龙PLC中批量读取位软元件,返回读取结果 /// /// 读取地址,具体的地址参考文档 /// 读取的长度 /// 带成功标志的结果数据对象 /// /// /// public OperateResult ReadBool(string address, ushort length) { // 获取指令 var command = OmronFinsNetHelper.BuildReadCommand(address, length, true); if (!command.IsSuccess) return OperateResult.CreateFailedResult(command); // 核心交互 OperateResult read = ReadBase(PackCommand(command.Content)); if (!read.IsSuccess) return OperateResult.CreateFailedResult(read); // 数据有效性分析 OperateResult valid = ResponseValidAnalysis(read.Content, true); if (!valid.IsSuccess) return OperateResult.CreateFailedResult(valid); // 返回正确的数据信息 return OperateResult.CreateSuccessResult(valid.Content.Select(m => m != 0x00 ? true : false).ToArray()); } /// /// 从欧姆龙PLC中批量读取位软元件,返回读取结果 /// /// 读取地址,具体的地址参考文档 /// 带成功标志的结果数据对象 /// /// 地址的格式请参照方法 /// /// /// /// public OperateResult ReadBool(string address) { OperateResult read = ReadBool(address, 1); if (!read.IsSuccess) return OperateResult.CreateFailedResult(read); return OperateResult.CreateSuccessResult(read.Content[0]); } /// /// 向PLC中位软元件写入bool数组,返回值说明,比如你写入D100,values[0]对应D100.0 /// /// 要写入的数据地址,具体的地址参考文档 /// 要写入的实际数据,长度为8的倍数 /// 返回写入结果 /// /// /// public OperateResult Write(string address, bool value) { return Write(address, new bool[] { value }); } /// /// 向PLC中位软元件写入bool数组,返回值说明,比如你写入D100,values[0]对应D100.0 /// /// 要写入的数据地址,具体的地址参考文档 /// 要写入的实际数据,可以指定任意的长度 /// 返回写入结果 /// /// /// public OperateResult Write(string address, bool[] values) { // 获取指令 var command = OmronFinsNetHelper.BuildWriteWordCommand(address, values.Select(m => m ? (byte)0x01 : (byte)0x00).ToArray(), true); ; if (!command.IsSuccess) return command; // 核心数据交互 OperateResult read = ReadBase(PackCommand(command.Content)); if (!read.IsSuccess) return read; // 数据有效性分析 OperateResult valid = ResponseValidAnalysis(read.Content, false); if (!valid.IsSuccess) return valid; // 成功 return OperateResult.CreateSuccessResult(); } #endregion #region Build Command /// /// 将普通的指令打包成完整的指令 /// /// fins指令 /// 完整的质量 private byte[] PackCommand(byte[] cmd) { cmd = Encoding.ASCII.GetBytes(ByteToHexString(cmd, (char)0)); byte[] buffer = new byte[18 + cmd.Length]; buffer[0] = (byte)'@'; buffer[1] = Melsec.MelsecHelper.BuildBytesFromData(this.UnitNumber)[0]; buffer[2] = Melsec.MelsecHelper.BuildBytesFromData(this.UnitNumber)[1]; buffer[3] = (byte)'F'; buffer[4] = (byte)'A'; buffer[5] = ResponseWaitTime; buffer[6] = Melsec.MelsecHelper.BuildBytesFromData(this.ICF)[0]; buffer[7] = Melsec.MelsecHelper.BuildBytesFromData(this.ICF)[1]; buffer[8] = Melsec.MelsecHelper.BuildBytesFromData(this.DA2)[0]; buffer[9] = Melsec.MelsecHelper.BuildBytesFromData(this.DA2)[1]; buffer[10] = Melsec.MelsecHelper.BuildBytesFromData(this.SA2)[0]; buffer[11] = Melsec.MelsecHelper.BuildBytesFromData(this.SA2)[1]; buffer[12] = Melsec.MelsecHelper.BuildBytesFromData(this.SID)[0]; buffer[13] = Melsec.MelsecHelper.BuildBytesFromData(this.SID)[1]; buffer[buffer.Length - 2] = (byte)'*'; buffer[buffer.Length - 1] = 0x0D; cmd.CopyTo(buffer, 14); // 计算FCS int tmp = buffer[0]; for (int i = 1; i < buffer.Length - 4; i++) { tmp = (tmp ^ buffer[i]); } buffer[buffer.Length - 4] = Melsec.MelsecHelper.BuildBytesFromData((byte)tmp)[0]; buffer[buffer.Length - 3] = Melsec.MelsecHelper.BuildBytesFromData((byte)tmp)[1]; string output = Encoding.ASCII.GetString(buffer); Console.WriteLine(output); return buffer; } /// /// 验证欧姆龙的Fins-TCP返回的数据是否正确的数据,如果正确的话,并返回所有的数据内容 /// /// 来自欧姆龙返回的数据内容 /// 是否读取 /// 带有是否成功的结果对象 public static OperateResult ResponseValidAnalysis(byte[] response, bool isRead) { // 数据有效性分析 if (response.Length >= 27) { // 提取错误码 int err = Convert.ToInt32(Encoding.ASCII.GetString(response, 19, 4)); byte[] Content = null; if (response.Length > 27) { Content =HexStringToBytes(Encoding.ASCII.GetString(response, 23, response.Length - 27)); } if (err > 0) return new OperateResult() { ErrorCode = err, Content = Content }; else { return OperateResult.CreateSuccessResult(Content); } } return new OperateResult(StringResources.Language.OmronReceiveDataError); } /// /// 字节数据转化成16进制表示的字符串 -> /// Byte data into a string of 16 binary representations /// /// 字节数组 /// 分割符 /// 返回的字符串 /// /// /// /// public static string ByteToHexString(byte[] InBytes, char segment) { StringBuilder sb = new StringBuilder(); foreach (byte InByte in InBytes) { if (segment == 0) sb.Append(string.Format("{0:X2}", InByte)); else sb.Append(string.Format("{0:X2}{1}", InByte, segment)); } if (segment != 0 && sb.Length > 1 && sb[sb.Length - 1] == segment) { sb.Remove(sb.Length - 1, 1); } return sb.ToString(); } /// /// 将16进制的字符串转化成Byte数据,将检测每2个字符转化,也就是说,中间可以是任意字符 -> /// Converts a 16-character string into byte data, which will detect every 2 characters converted, that is, the middle can be any character /// /// 十六进制的字符串,中间可以是任意的分隔符 /// 转换后的字节数组 /// 参数举例:AA 01 34 A8 /// /// /// public static byte[] HexStringToBytes(string hex) { hex = hex.ToUpper(); List hexCharList = new List() { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; MemoryStream ms = new MemoryStream(); for (int i = 0; i < hex.Length; i++) { if ((i + 1) < hex.Length) { if (hexCharList.Contains(hex[i]) && hexCharList.Contains(hex[i + 1])) { // 这是一个合格的字节数据 ms.WriteByte((byte)(hexCharList.IndexOf(hex[i]) * 16 + hexCharList.IndexOf(hex[i + 1]))); i++; } } } byte[] result = ms.ToArray(); ms.Dispose(); return result; } #endregion } }