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