1392 lines
47 KiB
C#
1392 lines
47 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Net.Sockets;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using static EasyModbus.TCPHandler;
|
||
|
||
namespace McProtocol.Mitsubishi
|
||
{
|
||
public class McProtocolTcp
|
||
{
|
||
/// <summary>
|
||
/// 使用的指令框架
|
||
/// </summary>
|
||
public McFrame CommandFrame { get; set; }
|
||
private McCommand Command { get; set; }
|
||
/// <summary>
|
||
/// 主机名或者IP地址
|
||
/// </summary>
|
||
public string HostName { get; set; }
|
||
/// <summary>
|
||
/// 端口号
|
||
/// </summary>
|
||
public int PortNumber { get; set; }
|
||
public int Device { private set; get; }
|
||
|
||
private const int BlockSize = 0x0010;
|
||
private TcpClient Client { get; set; }
|
||
private NetworkStream Stream { get; set; }
|
||
|
||
// 是否连接
|
||
public bool Connected
|
||
{
|
||
get
|
||
{
|
||
return Client.Connected;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 构造函数
|
||
/// </summary>
|
||
public McProtocolTcp() : this("", 0, McFrame.MC3E)
|
||
{
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 构造函数
|
||
/// </summary>
|
||
/// <param name="iHostName"></param>
|
||
/// <param name="iPortNumber"></param>
|
||
/// <param name="frame"></param>
|
||
public McProtocolTcp(string iHostName, int iPortNumber, McFrame frame)
|
||
{
|
||
CommandFrame = frame;
|
||
Client = new TcpClient();
|
||
|
||
//C70 = MC3E
|
||
HostName = iHostName;
|
||
PortNumber = iPortNumber;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 后处理
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
TcpClient c = Client;
|
||
if (c.Connected)
|
||
{
|
||
c.Close();
|
||
}
|
||
}
|
||
|
||
public void Open()
|
||
{
|
||
if (!Client.Connected)
|
||
{
|
||
// 实现Keep Alive功能
|
||
var ka = new List<byte>(sizeof(uint) * 3);
|
||
ka.AddRange(BitConverter.GetBytes(1u));
|
||
ka.AddRange(BitConverter.GetBytes(45000u));
|
||
ka.AddRange(BitConverter.GetBytes(5000u));
|
||
Client.Client.IOControl(IOControlCode.KeepAliveValues, ka.ToArray(), null);
|
||
|
||
IAsyncResult asyncResult = Client.BeginConnect(HostName, PortNumber, null, null);
|
||
if (!asyncResult.AsyncWaitHandle.WaitOne(1000))
|
||
{
|
||
throw new Exception("连接超时");
|
||
}
|
||
Client.EndConnect(asyncResult);
|
||
Stream = Client.GetStream();
|
||
}
|
||
Command = new McCommand(CommandFrame);
|
||
}
|
||
|
||
|
||
public int SetBitDevice(string iDeviceName, int iSize, int[] iData)
|
||
{
|
||
PlcDeviceType type;
|
||
int addr;
|
||
GetDeviceCode(iDeviceName, out type, out addr);
|
||
return SetBitDevice(type, addr, iSize, iData);
|
||
}
|
||
|
||
public int SetBitDevice(PlcDeviceType iType, int iAddress, int iSize, int[] iData)
|
||
{
|
||
var type = iType;
|
||
var addr = iAddress;
|
||
var data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, (byte)iSize, (byte)(iSize >> 8) };
|
||
var d = (byte)iData[0];
|
||
var i = 0;
|
||
while (i < iData.Length)
|
||
{
|
||
if (i % 2 == 0)
|
||
{
|
||
d = (byte)iData[i];
|
||
d <<= 4;
|
||
}
|
||
else
|
||
{
|
||
d |= (byte)(iData[i] & 0x01);
|
||
data.Add(d);
|
||
}
|
||
++i;
|
||
}
|
||
if (i % 2 != 0)
|
||
{
|
||
data.Add(d);
|
||
}
|
||
int length = (int)Command.FrameType;// == McFrame.MC3E) ? 11 : 15;
|
||
byte[] sdCommand = Command.SetCommandMC3E(0x1401, 0x0001, data.ToArray());
|
||
byte[] rtResponse = TryExecution(sdCommand, length);
|
||
int rtCode = Command.SetResponse(rtResponse);
|
||
return rtCode;
|
||
}
|
||
|
||
public int GetBitDevice(string iDeviceName, int iSize, int[] oData)
|
||
{
|
||
PlcDeviceType type;
|
||
int addr;
|
||
GetDeviceCode(iDeviceName, out type, out addr);
|
||
return GetBitDevice(type, addr, iSize, oData);
|
||
}
|
||
|
||
public int GetBitDevice(PlcDeviceType iType, int iAddress, int iSize, int[] oData)
|
||
{
|
||
|
||
PlcDeviceType type = iType;
|
||
int addr = iAddress;
|
||
var data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, (byte)iSize, (byte)(iSize >> 8) };
|
||
byte[] sdCommand = Command.SetCommandMC3E(0x0401, 0x0001, data.ToArray());
|
||
int length = (Command.FrameType == McFrame.MC3E) ? 11 : 15;
|
||
byte[] rtResponse = TryExecution(sdCommand, length);
|
||
int rtCode = Command.SetResponse(rtResponse);
|
||
byte[] rtData = Command.Response;
|
||
for (int i = 0; i < iSize; ++i)
|
||
{
|
||
if (i % 2 == 0)
|
||
{
|
||
oData[i] = (rtCode == 0) ? ((rtData[i / 2] >> 4) & 0x01) : 0;
|
||
}
|
||
else
|
||
{
|
||
oData[i] = (rtCode == 0) ? (rtData[i / 2] & 0x01) : 0;
|
||
}
|
||
}
|
||
return rtCode;
|
||
}
|
||
|
||
public int WriteDeviceBlock(string iDeviceName, int iSize, ushort[] iData)
|
||
{
|
||
PlcDeviceType type;
|
||
int addr;
|
||
GetDeviceCode(iDeviceName, out type, out addr);
|
||
return WriteDeviceBlock(type, addr, iSize, iData);
|
||
}
|
||
|
||
public int WriteDeviceBlock(PlcDeviceType iType, int iAddress, int iSize, ushort[] iData)
|
||
{
|
||
PlcDeviceType type = iType;
|
||
int addr = iAddress;
|
||
List<byte> data;
|
||
List<byte> DeviceData = new List<byte>();
|
||
foreach (ushort t in iData)
|
||
{
|
||
byte[] value = BitConverter.GetBytes(t);
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(value);
|
||
}
|
||
DeviceData.AddRange(value);
|
||
}
|
||
byte[] sdCommand;
|
||
int length;
|
||
switch (CommandFrame)
|
||
{
|
||
case McFrame.MC3E:
|
||
data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, (byte)iSize, (byte)(iSize >> 8) };
|
||
data.AddRange(DeviceData.ToArray());
|
||
sdCommand = Command.SetCommandMC3E(0x1401, 0x0000, data.ToArray());
|
||
length = 11;
|
||
break;
|
||
case McFrame.MC4E:
|
||
data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, (byte)iSize, (byte)(iSize >> 8) };
|
||
data.AddRange(DeviceData.ToArray());
|
||
sdCommand = Command.SetCommandMC4E(0x1401, 0x0000, data.ToArray());
|
||
length = 15;
|
||
break;
|
||
case McFrame.MC1E:
|
||
data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)(addr >> 24), 0x20, 0x44, (byte)iSize, 0x00 };
|
||
data.AddRange(DeviceData.ToArray());
|
||
//Add data
|
||
sdCommand = Command.SetCommandMC1E(0x03, data.ToArray());
|
||
length = 2;
|
||
break;
|
||
default:
|
||
throw new Exception("Message frame not supported");
|
||
}
|
||
|
||
//TEST take care of the writing
|
||
byte[] rtResponse = TryExecution(sdCommand, length);
|
||
int rtCode = Command.SetResponse(rtResponse);
|
||
return rtCode;
|
||
}
|
||
|
||
public byte[] ReadDeviceBlock(string iDeviceName, int iSize, ushort[] oData)
|
||
{
|
||
PlcDeviceType type;
|
||
int addr;
|
||
GetDeviceCode(iDeviceName, out type, out addr);
|
||
return ReadDeviceBlock(type, addr, iSize, oData);
|
||
}
|
||
|
||
public byte[] ReadDeviceBlock(PlcDeviceType iType, int iAddress, int iSize, ushort[] oData)
|
||
{
|
||
|
||
PlcDeviceType type = iType;
|
||
int addr = iAddress;
|
||
List<byte> data;
|
||
byte[] sdCommand;
|
||
int length;
|
||
|
||
switch (CommandFrame)
|
||
{
|
||
case McFrame.MC3E:
|
||
data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, (byte)iSize, (byte)(iSize >> 8) };
|
||
sdCommand = Command.SetCommandMC3E(0x0401, 0x0000, data.ToArray());
|
||
length = 11;
|
||
break;
|
||
case McFrame.MC4E:
|
||
data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, (byte)iSize, (byte)(iSize >> 8) };
|
||
sdCommand = Command.SetCommandMC4E(0x0401, 0x0000, data.ToArray());
|
||
length = 15;
|
||
break;
|
||
case McFrame.MC1E:
|
||
data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)(addr >> 24), 0x20, 0x44, (byte)iSize, 0x00 };
|
||
sdCommand = Command.SetCommandMC1E(0x01, data.ToArray());
|
||
length = 2;
|
||
break;
|
||
default:
|
||
throw new Exception("Message frame not supported");
|
||
}
|
||
|
||
byte[] rtResponse = TryExecution(sdCommand, length);
|
||
int rtCode = Command.SetResponse(rtResponse);
|
||
byte[] rtData = Command.Response;
|
||
for (int i = 0; i < iSize; ++i)
|
||
{
|
||
if (rtCode == 0)
|
||
{
|
||
oData[i] = (ushort)(rtData[i * 2] << 8 | rtData[i * 2 + 1]);
|
||
}
|
||
//oData[i] = (rtCode == 0) ? BitConverter.ToUInt16(rtData, i * 2) : 0;
|
||
}
|
||
return rtData;
|
||
}
|
||
|
||
public int SetDevice(string iDeviceName, int iData)
|
||
{
|
||
PlcDeviceType type;
|
||
int addr;
|
||
GetDeviceCode(iDeviceName, out type, out addr);
|
||
return SetDevice(type, addr, iData);
|
||
}
|
||
|
||
public int SetDevice(PlcDeviceType iType, int iAddress, int iData)
|
||
{
|
||
PlcDeviceType type = iType;
|
||
int addr = iAddress;
|
||
var data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, 0x01, 0x00, (byte)iData, (byte)(iData >> 8) };
|
||
byte[] sdCommand = Command.SetCommandMC3E(0x1401, 0x0000, data.ToArray());
|
||
int length = (Command.FrameType == McFrame.MC3E) ? 11 : 15;
|
||
byte[] rtResponse = TryExecution(sdCommand, length);
|
||
int rtCode = Command.SetResponse(rtResponse);
|
||
return rtCode;
|
||
}
|
||
|
||
public int GetDevice(string iDeviceName)
|
||
{
|
||
PlcDeviceType type;
|
||
int addr;
|
||
GetDeviceCode(iDeviceName, out type, out addr);
|
||
return GetDevice(type, addr);
|
||
}
|
||
|
||
public int GetDevice(PlcDeviceType iType, int iAddress)
|
||
{
|
||
PlcDeviceType type = iType;
|
||
int addr = iAddress;
|
||
var data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, 0x01, 0x00 };
|
||
byte[] sdCommand = Command.SetCommandMC3E(0x0401, 0x0000, data.ToArray());
|
||
int length = (Command.FrameType == McFrame.MC3E) ? 11 : 15;
|
||
byte[] rtResponse = TryExecution(sdCommand, length);
|
||
int rtCode = Command.SetResponse(rtResponse);
|
||
if (0 < rtCode)
|
||
{
|
||
this.Device = 0;
|
||
}
|
||
else
|
||
{
|
||
byte[] rtData = Command.Response;
|
||
this.Device = BitConverter.ToInt16(rtData, 0);
|
||
}
|
||
return rtCode;
|
||
}
|
||
|
||
#region IPlcEx
|
||
/// <summary>
|
||
/// 得到字节数据
|
||
/// </summary>
|
||
/// <param name="iDeviceName">如:M1000</param>
|
||
/// <param name="iSize">读取长度:>0</param>
|
||
/// <returns>数据</returns>
|
||
public int[] GetBitDevice(string iDeviceName, int iSize = 1)
|
||
{
|
||
int[] oData = new int[iSize];
|
||
GetBitDevice(iDeviceName, iSize, oData);
|
||
return oData;
|
||
}
|
||
/// <summary>
|
||
/// 得到字节数据
|
||
/// </summary>
|
||
/// <param name="iType">设备类型,如:M</param>
|
||
/// <param name="iAddress">如:M1000</param>
|
||
/// <param name="iSize">读取长度:>0</param>
|
||
/// <returns>数据</returns>
|
||
public int[] GetBitDevice(PlcDeviceType iType, int iAddress, int iSize = 1)
|
||
{
|
||
int[] oData = new int[iSize];
|
||
GetBitDevice(iType, iAddress, iSize, oData);
|
||
return oData;
|
||
}
|
||
/// <summary>
|
||
/// 设置字节数据
|
||
/// </summary>
|
||
/// <param name="iDeviceName">如:M1000</param>
|
||
/// <param name="iData">设置的数据</param>
|
||
public void SetBitDevice(string iDeviceName, params int[] iData)
|
||
{
|
||
SetBitDevice(iDeviceName, iData.Length, iData);
|
||
}
|
||
/// <summary>
|
||
/// 设置字节数据
|
||
/// </summary>
|
||
/// <param name="iType">设备类型,如:M</param>
|
||
/// <param name="iAddress">如:M1000</param>
|
||
/// <param name="iData">设置的数据</param>
|
||
public void SetBitDevice(PlcDeviceType iType, int iAddress, params int[] iData)
|
||
{
|
||
SetBitDevice(iType, iAddress, iData.Length, iData);
|
||
}
|
||
/// <summary>
|
||
/// 得到数据块
|
||
/// </summary>
|
||
/// <param name="iDeviceName">如:D1000</param>
|
||
/// <param name="iSize">读取长度:>0</param>
|
||
/// <returns>数据</returns>
|
||
public ushort[] ReadDeviceBlock(string iDeviceName, int iSize = 1)
|
||
{
|
||
ushort[] oData = new ushort[iSize];
|
||
ReadDeviceBlock(iDeviceName, iSize, oData);
|
||
return oData;
|
||
}
|
||
/// <summary>
|
||
/// 得到数据块
|
||
/// </summary>
|
||
/// <param name="iType">设备类型,如:D</param>
|
||
/// <param name="iAddress">如:D1000</param>
|
||
/// <param name="iSize">读取长度:>0</param>
|
||
/// <returns>数据</returns>
|
||
public ushort[] ReadDeviceBlock(PlcDeviceType iType, int iAddress, int iSize = 1)
|
||
{
|
||
ushort[] oData = new ushort[iSize];
|
||
ReadDeviceBlock(iType, iAddress, iSize, oData);
|
||
return oData;
|
||
}
|
||
/// <summary>
|
||
/// 写入数据块
|
||
/// </summary>
|
||
/// <param name="iDeviceName">如:D1000</param>
|
||
/// <param name="iData">写入10进制数据</param>
|
||
public void WriteDeviceBlock(string iDeviceName, params ushort[] iData)
|
||
{
|
||
WriteDeviceBlock(iDeviceName, iData.Length, iData);
|
||
}
|
||
/// <summary>
|
||
/// 写入数据块
|
||
/// </summary>
|
||
/// <param name="iType">设备类型,如:D</param>
|
||
/// <param name="iAddress">如:D1000</param>
|
||
/// <param name="iData">写入10进制数据</param>
|
||
public void WriteDeviceBlock(PlcDeviceType iType, int iAddress, params ushort[] iData)
|
||
{
|
||
WriteDeviceBlock(iType, iAddress, iData.Length, iData);
|
||
}
|
||
#endregion
|
||
|
||
//public int GetCpuType(out string oCpuName, out int oCpuType)
|
||
//{
|
||
// int rtCode = Command.Execute(0x0101, 0x0000, new byte[0]);
|
||
// oCpuName = "dummy";
|
||
// oCpuType = 0;
|
||
// return rtCode;
|
||
//}
|
||
|
||
public PlcDeviceType GetDeviceType(string s)
|
||
{
|
||
return (s == "M") ? PlcDeviceType.M :
|
||
(s == "SM") ? PlcDeviceType.SM :
|
||
(s == "L") ? PlcDeviceType.L :
|
||
(s == "F") ? PlcDeviceType.F :
|
||
(s == "V") ? PlcDeviceType.V :
|
||
(s == "S") ? PlcDeviceType.S :
|
||
(s == "X") ? PlcDeviceType.X :
|
||
(s == "Y") ? PlcDeviceType.Y :
|
||
(s == "B") ? PlcDeviceType.B :
|
||
(s == "SB") ? PlcDeviceType.SB :
|
||
(s == "DX") ? PlcDeviceType.DX :
|
||
(s == "DY") ? PlcDeviceType.DY :
|
||
(s == "D") ? PlcDeviceType.D :
|
||
(s == "SD") ? PlcDeviceType.SD :
|
||
(s == "R") ? PlcDeviceType.R :
|
||
(s == "ZR") ? PlcDeviceType.ZR :
|
||
(s == "W") ? PlcDeviceType.W :
|
||
(s == "SW") ? PlcDeviceType.SW :
|
||
(s == "TC") ? PlcDeviceType.TC :
|
||
(s == "TS") ? PlcDeviceType.TS :
|
||
(s == "TN") ? PlcDeviceType.TN :
|
||
(s == "CC") ? PlcDeviceType.CC :
|
||
(s == "CS") ? PlcDeviceType.CS :
|
||
(s == "CN") ? PlcDeviceType.CN :
|
||
(s == "SC") ? PlcDeviceType.SC :
|
||
(s == "SS") ? PlcDeviceType.SS :
|
||
(s == "SN") ? PlcDeviceType.SN :
|
||
(s == "Z") ? PlcDeviceType.Z :
|
||
(s == "TT") ? PlcDeviceType.TT :
|
||
(s == "TM") ? PlcDeviceType.TM :
|
||
(s == "CT") ? PlcDeviceType.CT :
|
||
(s == "CM") ? PlcDeviceType.CM :
|
||
(s == "A") ? PlcDeviceType.A :
|
||
PlcDeviceType.Max;
|
||
}
|
||
|
||
public static bool IsBitDevice(PlcDeviceType type)
|
||
{
|
||
return !((type == PlcDeviceType.D) || (type == PlcDeviceType.SD) || (type == PlcDeviceType.Z) || (type == PlcDeviceType.ZR) || (type == PlcDeviceType.R) || (type == PlcDeviceType.W));
|
||
}
|
||
|
||
public bool IsHexDevice(PlcDeviceType type)
|
||
{
|
||
return (type == PlcDeviceType.X) || (type == PlcDeviceType.Y) || (type == PlcDeviceType.B) || (type == PlcDeviceType.W);
|
||
}
|
||
|
||
public void GetDeviceCode(string iDeviceName, out PlcDeviceType oType, out int oAddress)
|
||
{
|
||
string s = iDeviceName.ToUpper();
|
||
string strAddress;
|
||
|
||
// 1.取出一个字符
|
||
string strType = s.Substring(0, 1);
|
||
switch (strType)
|
||
{
|
||
case "A":
|
||
case "B":
|
||
case "D":
|
||
case "F":
|
||
case "L":
|
||
case "M":
|
||
case "R":
|
||
case "V":
|
||
case "W":
|
||
case "X":
|
||
case "Y":
|
||
// 2字母后面,它应该是一个数字,所以转换它
|
||
strAddress = s.Substring(1);
|
||
break;
|
||
case "Z":
|
||
// 再拿出一个字符
|
||
strType = s.Substring(0, 2);
|
||
// 对于文件寄存器 : 2
|
||
// 对于索引寄存器 : 1
|
||
strAddress = s.Substring(strType.Equals("ZR") ? 2 : 1);
|
||
break;
|
||
case "C":
|
||
// 再拿出一个字符
|
||
strType = s.Substring(0, 2);
|
||
switch (strType)
|
||
{
|
||
case "CC":
|
||
case "CM":
|
||
case "CN":
|
||
case "CS":
|
||
case "CT":
|
||
strAddress = s.Substring(2);
|
||
break;
|
||
default:
|
||
throw new Exception("Invalid format.");
|
||
}
|
||
break;
|
||
case "S":
|
||
// 再拿出一个字符
|
||
strType = s.Substring(0, 2);
|
||
switch (strType)
|
||
{
|
||
case "SD":
|
||
case "SM":
|
||
strAddress = s.Substring(2);
|
||
break;
|
||
default:
|
||
throw new Exception("Invalid format.");
|
||
}
|
||
break;
|
||
case "T":
|
||
// 再拿出一个字符
|
||
strType = s.Substring(0, 2);
|
||
switch (strType)
|
||
{
|
||
case "TC":
|
||
case "TM":
|
||
case "TN":
|
||
case "TS":
|
||
case "TT":
|
||
strAddress = s.Substring(2);
|
||
break;
|
||
default:
|
||
throw new Exception("Invalid format.");
|
||
}
|
||
break;
|
||
default:
|
||
throw new Exception("Invalid format.");
|
||
}
|
||
oType = GetDeviceType(strType);
|
||
oAddress = IsHexDevice(oType) ? Convert.ToInt32(strAddress, BlockSize) : Convert.ToInt32(strAddress);
|
||
}
|
||
|
||
|
||
private byte[] TryExecution(byte[] iCommand, int minlength)
|
||
{
|
||
byte[] rtResponse;
|
||
int tCount = 10;
|
||
do
|
||
{
|
||
rtResponse = Execute(iCommand);
|
||
--tCount;
|
||
if (tCount < 0)
|
||
{
|
||
throw new Exception("无法从 PLC 获取正确的值.");
|
||
}
|
||
} while (Command.IsIncorrectResponse(rtResponse, minlength));
|
||
return rtResponse;
|
||
}
|
||
|
||
// 表示用于通信的命令的内部类
|
||
class McCommand
|
||
{
|
||
public McFrame FrameType { get; private set; } // 框架类型
|
||
private uint SerialNumber { get; set; } // 序号
|
||
private uint NetworkNumber { get; set; } // 网络号码
|
||
private uint PcNumber { get; set; } // 电脑号码
|
||
private uint IoNumber { get; set; } // 请求单元 I/O 编号/O番号
|
||
private uint ChannelNumber { get; set; } // 请求单位站号
|
||
private uint CpuTimer { get; set; } // 中央处理器监控计时器
|
||
private int ResultCode { get; set; } // 退出代码
|
||
public byte[] Response { get; private set; } // 响应数据
|
||
|
||
// 构造 函数
|
||
public McCommand(McFrame iFrame)
|
||
{
|
||
FrameType = iFrame;
|
||
SerialNumber = 0x0001u;
|
||
NetworkNumber = 0x0000u;
|
||
PcNumber = 0x00FFu;
|
||
IoNumber = 0x03FFu;
|
||
ChannelNumber = 0x0000u;
|
||
CpuTimer = 0x0010u;
|
||
}
|
||
|
||
public byte[] SetCommandMC1E(byte Subheader, byte[] iData)
|
||
{
|
||
List<byte> ret = new List<byte>(iData.Length + 4);
|
||
ret.Add(Subheader);
|
||
ret.Add((byte)this.PcNumber);
|
||
ret.Add((byte)CpuTimer);
|
||
ret.Add((byte)(CpuTimer >> 8));
|
||
ret.AddRange(iData);
|
||
return ret.ToArray();
|
||
}
|
||
public byte[] SetCommandMC3E(uint iMainCommand, uint iSubCommand, byte[] iData)
|
||
{
|
||
var dataLength = (uint)(iData.Length + 6);
|
||
List<byte> ret = new List<byte>(iData.Length + 20);
|
||
uint frame = 0x0050u;
|
||
ret.Add((byte)frame);
|
||
ret.Add((byte)(frame >> 8));
|
||
ret.Add((byte)NetworkNumber);
|
||
ret.Add((byte)PcNumber);
|
||
ret.Add((byte)IoNumber);
|
||
ret.Add((byte)(IoNumber >> 8));
|
||
ret.Add((byte)ChannelNumber);
|
||
ret.Add((byte)dataLength);
|
||
ret.Add((byte)(dataLength >> 8));
|
||
ret.Add((byte)CpuTimer);
|
||
ret.Add((byte)(CpuTimer >> 8));
|
||
ret.Add((byte)iMainCommand);
|
||
ret.Add((byte)(iMainCommand >> 8));
|
||
ret.Add((byte)iSubCommand);
|
||
ret.Add((byte)(iSubCommand >> 8));
|
||
|
||
ret.AddRange(iData);
|
||
return ret.ToArray();
|
||
}
|
||
public byte[] SetCommandMC4E(uint iMainCommand, uint iSubCommand, byte[] iData)
|
||
{
|
||
var dataLength = (uint)(iData.Length + 6);
|
||
var ret = new List<byte>(iData.Length + 20);
|
||
uint frame = 0x0054u;
|
||
ret.Add((byte)frame);
|
||
ret.Add((byte)(frame >> 8));
|
||
ret.Add((byte)SerialNumber);
|
||
ret.Add((byte)(SerialNumber >> 8));
|
||
ret.Add(0x00);
|
||
ret.Add(0x00);
|
||
ret.Add((byte)NetworkNumber);
|
||
ret.Add((byte)PcNumber);
|
||
ret.Add((byte)IoNumber);
|
||
ret.Add((byte)(IoNumber >> 8));
|
||
ret.Add((byte)ChannelNumber);
|
||
ret.Add((byte)dataLength);
|
||
ret.Add((byte)(dataLength >> 8));
|
||
ret.Add((byte)CpuTimer);
|
||
ret.Add((byte)(CpuTimer >> 8));
|
||
ret.Add((byte)iMainCommand);
|
||
ret.Add((byte)(iMainCommand >> 8));
|
||
ret.Add((byte)iSubCommand);
|
||
ret.Add((byte)(iSubCommand >> 8));
|
||
ret.AddRange(iData);
|
||
return ret.ToArray();
|
||
}
|
||
public int SetResponse(byte[] iResponse)
|
||
{
|
||
int min;
|
||
switch (FrameType)
|
||
{
|
||
case McFrame.MC1E:
|
||
min = 2;
|
||
if (min <= iResponse.Length)
|
||
{
|
||
ResultCode = (int)iResponse[min - 2];
|
||
Response = new byte[iResponse.Length - 2];
|
||
Buffer.BlockCopy(iResponse, min, Response, 0, Response.Length);
|
||
}
|
||
break;
|
||
case McFrame.MC3E:
|
||
min = 11;
|
||
if (min <= iResponse.Length)
|
||
{
|
||
var btCount = new[] { iResponse[min - 4], iResponse[min - 3] };
|
||
var btCode = new[] { iResponse[min - 2], iResponse[min - 1] };
|
||
int rsCount = BitConverter.ToUInt16(btCount, 0);
|
||
ResultCode = BitConverter.ToUInt16(btCode, 0);
|
||
Response = new byte[rsCount - 2];
|
||
Buffer.BlockCopy(iResponse, min, Response, 0, Response.Length);
|
||
}
|
||
break;
|
||
case McFrame.MC4E:
|
||
min = 15;
|
||
if (min <= iResponse.Length)
|
||
{
|
||
var btCount = new[] { iResponse[min - 4], iResponse[min - 3] };
|
||
var btCode = new[] { iResponse[min - 2], iResponse[min - 1] };
|
||
int rsCount = BitConverter.ToUInt16(btCount, 0);
|
||
ResultCode = BitConverter.ToUInt16(btCode, 0);
|
||
Response = new byte[rsCount - 2];
|
||
Buffer.BlockCopy(iResponse, min, Response, 0, Response.Length);
|
||
}
|
||
break;
|
||
default:
|
||
throw new Exception("Frame type not supported.");
|
||
|
||
}
|
||
return ResultCode;
|
||
}
|
||
public bool IsIncorrectResponse(byte[] iResponse, int minLenght)
|
||
{
|
||
//TEST add 1E frame
|
||
switch (this.FrameType)
|
||
{
|
||
case McFrame.MC1E: return ((iResponse.Length < minLenght));
|
||
|
||
case McFrame.MC3E:
|
||
case McFrame.MC4E:
|
||
var btCount = new[] { iResponse[minLenght - 4], iResponse[minLenght - 3] };
|
||
var btCode = new[] { iResponse[minLenght - 2], iResponse[minLenght - 1] };
|
||
var rsCount = BitConverter.ToUInt16(btCount, 0) - 2;
|
||
var rsCode = BitConverter.ToUInt16(btCode, 0);
|
||
return (rsCode == 0 && rsCount != (iResponse.Length - minLenght));
|
||
|
||
default: throw new Exception("Type Not supported");
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
private readonly object balanceLock = new object();
|
||
protected byte[] Execute(byte[] iCommand)
|
||
{
|
||
lock (balanceLock)
|
||
{
|
||
List<byte> list = new List<byte>();
|
||
|
||
NetworkStream ns = Stream;
|
||
ns.Write(iCommand, 0, iCommand.Length);
|
||
ns.Flush();
|
||
|
||
using (var ms = new MemoryStream())
|
||
{
|
||
var buff = new byte[256];
|
||
do
|
||
{
|
||
int sz = ns.Read(buff, 0, buff.Length);
|
||
if (sz == 0)
|
||
{
|
||
throw new Exception("已断开连接");
|
||
}
|
||
ms.Write(buff, 0, sz);
|
||
}
|
||
while (ns.DataAvailable);
|
||
|
||
return ms.ToArray();
|
||
}
|
||
}
|
||
}
|
||
|
||
#region INT16
|
||
public short ReadInt16(string address)
|
||
{
|
||
try
|
||
{
|
||
return ReadInt16(address, 1)[0];
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public short[] ReadInt16(string address, int count = 1)
|
||
{
|
||
try
|
||
{
|
||
//地址解析
|
||
ushort[] value = ReadDeviceBlock(address, count);//一个地址2个字节
|
||
short[] values = new short[count];
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
byte[] data = BitConverter.GetBytes(value[i]);
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
values[i] = BitConverter.ToInt16(data, 0);
|
||
}
|
||
return values;
|
||
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
public void WriteInt16(string address, short value)
|
||
{
|
||
try
|
||
{
|
||
WriteInt16(address, new short[] { value });
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public void WriteInt16(string address, short[] value)
|
||
{
|
||
try
|
||
{
|
||
ushort[] values = new ushort[value.Length];
|
||
for (int i = 0; i < value.Length; i++)
|
||
{
|
||
byte[] data = BitConverter.GetBytes(value[i]);
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
values[i] = BitConverter.ToUInt16(data, 0);
|
||
}
|
||
WriteDeviceBlock(address, values);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region UINT16
|
||
public ushort ReadUInt16(string address)
|
||
{
|
||
try
|
||
{
|
||
return ReadUInt16(address, 1)[0];
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public ushort[] ReadUInt16(string address, int count = 1)
|
||
{
|
||
try
|
||
{
|
||
//地址解析
|
||
ushort[] value = ReadDeviceBlock(address, count);//一个地址2个字节
|
||
ushort[] values = new ushort[count];
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
byte[] data = BitConverter.GetBytes(value[i]);
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
values[i] = BitConverter.ToUInt16(data, 0);
|
||
}
|
||
return values;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
public void WriteUInt16(string address, ushort value)
|
||
{
|
||
try
|
||
{
|
||
WriteUInt16(address, new ushort[] { value });
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public void WriteUInt16(string address, ushort[] value)
|
||
{
|
||
try
|
||
{
|
||
ushort[] values = new ushort[value.Length];
|
||
for (int i = 0; i < value.Length; i++)
|
||
{
|
||
byte[] data = BitConverter.GetBytes(value[i]);
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
values[i] = BitConverter.ToUInt16(data, 0);
|
||
}
|
||
WriteDeviceBlock(address, values);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region INT32
|
||
public int ReadInt32(string address)
|
||
{
|
||
try
|
||
{
|
||
return ReadInt32(address, 1)[0];
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public int[] ReadInt32(string address, int count = 1)
|
||
{
|
||
try
|
||
{
|
||
//地址解析
|
||
ushort[] value = ReadDeviceBlock(address, count * 2);//一个地址2个字节
|
||
int[] values = new int[count];
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
byte[] data1 = BitConverter.GetBytes(value[2 * i + 0]);
|
||
byte[] data2 = BitConverter.GetBytes(value[2 * i + 1]);
|
||
List<byte> list = new List<byte>();
|
||
list.AddRange(data1);
|
||
list.AddRange(data2);
|
||
byte[] data = list.ToArray();
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
values[i] = BitConverter.ToInt32(data, 0);
|
||
}
|
||
return values;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
public void WriteInt32(string address, int value)
|
||
{
|
||
try
|
||
{
|
||
WriteInt32(address, new int[] { value });
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public void WriteInt32(string address, int[] value)
|
||
{
|
||
try
|
||
{
|
||
List<ushort> values = new List<ushort>();
|
||
for (int i = 0; i < value.Length; i++)
|
||
{
|
||
byte[] data = BitConverter.GetBytes(value[i]);
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
ushort value1 = BitConverter.ToUInt16(data, 0);
|
||
ushort value2 = BitConverter.ToUInt16(data, 2);
|
||
values.Add(value1);
|
||
values.Add(value2);
|
||
}
|
||
WriteDeviceBlock(address, values.ToArray());
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region UINT32
|
||
public uint ReadUInt32(string address)
|
||
{
|
||
try
|
||
{
|
||
return ReadUInt32(address, 1)[0];
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public uint[] ReadUInt32(string address, int count = 1)
|
||
{
|
||
try
|
||
{
|
||
//地址解析
|
||
ushort[] value = ReadDeviceBlock(address, count * 2);//一个地址2个字节
|
||
uint[] values = new uint[count];
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
byte[] data1 = BitConverter.GetBytes(value[2 * i + 0]);
|
||
byte[] data2 = BitConverter.GetBytes(value[2 * i + 1]);
|
||
List<byte> list = new List<byte>();
|
||
list.AddRange(data1);
|
||
list.AddRange(data2);
|
||
byte[] data = list.ToArray();
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
values[i] = BitConverter.ToUInt32(data, 0);
|
||
}
|
||
return values;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
public void WriteUInt32(string address, uint value)
|
||
{
|
||
try
|
||
{
|
||
WriteUInt32(address, new uint[] { value });
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public void WriteUInt32(string address, uint[] value)
|
||
{
|
||
try
|
||
{
|
||
List<ushort> values = new List<ushort>();
|
||
for (int i = 0; i < value.Length; i++)
|
||
{
|
||
byte[] data = BitConverter.GetBytes(value[i]);
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
ushort value1 = BitConverter.ToUInt16(data, 0);
|
||
ushort value2 = BitConverter.ToUInt16(data, 2);
|
||
values.Add(value1);
|
||
values.Add(value2);
|
||
}
|
||
WriteDeviceBlock(address, values.ToArray());
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region FLOAT
|
||
public float ReadFloat(string address)
|
||
{
|
||
try
|
||
{
|
||
return ReadFloat(address, 1)[0];
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public float[] ReadFloat(string address, int count = 1)
|
||
{
|
||
try
|
||
{
|
||
//地址解析
|
||
ushort[] value = ReadDeviceBlock(address, count * 2);//一个地址2个字节
|
||
float[] values = new float[count];
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
byte[] data1 = BitConverter.GetBytes(value[2 * i + 0]);
|
||
byte[] data2 = BitConverter.GetBytes(value[2 * i + 1]);
|
||
List<byte> list = new List<byte>();
|
||
list.AddRange(data1);
|
||
list.AddRange(data2);
|
||
byte[] data = list.ToArray();
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
values[i] = BitConverter.ToSingle(data, 0);
|
||
}
|
||
return values;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
public void WriteFloat(string address, float value)
|
||
{
|
||
try
|
||
{
|
||
WriteFloat(address, new float[] { value });
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public void WriteFloat(string address, float[] value)
|
||
{
|
||
try
|
||
{
|
||
List<ushort> values = new List<ushort>();
|
||
for (int i = 0; i < value.Length; i++)
|
||
{
|
||
byte[] data = BitConverter.GetBytes(value[i]);
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
ushort value1 = BitConverter.ToUInt16(data, 0);
|
||
ushort value2 = BitConverter.ToUInt16(data, 2);
|
||
values.Add(value1);
|
||
values.Add(value2);
|
||
}
|
||
WriteDeviceBlock(address, values.ToArray());
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
|
||
#region Double
|
||
public double ReadDouble(string address)
|
||
{
|
||
try
|
||
{
|
||
return ReadDouble(address, 1)[0];
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public double[] ReadDouble(string address, int count = 1)
|
||
{
|
||
try
|
||
{
|
||
//地址解析
|
||
ushort[] value = ReadDeviceBlock(address, count * 4);
|
||
double[] values = new double[count];
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
byte[] data1 = BitConverter.GetBytes(value[4 * i + 0]);
|
||
byte[] data2 = BitConverter.GetBytes(value[4 * i + 1]);
|
||
byte[] data3 = BitConverter.GetBytes(value[4 * i + 2]);
|
||
byte[] data4 = BitConverter.GetBytes(value[4 * i + 3]);
|
||
List<byte> list = new List<byte>();
|
||
list.AddRange(data1);
|
||
list.AddRange(data2);
|
||
list.AddRange(data3);
|
||
list.AddRange(data4);
|
||
byte[] data = list.ToArray();
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
values[i] = BitConverter.ToDouble(data, 0);
|
||
}
|
||
return values;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
public void WriteDouble(string address, double value)
|
||
{
|
||
try
|
||
{
|
||
WriteDouble(address, new double[] { value });
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public void WriteDouble(string address, double[] value)
|
||
{
|
||
try
|
||
{
|
||
List<ushort> values = new List<ushort>();
|
||
for (int i = 0; i < value.Length; i++)
|
||
{
|
||
byte[] data = BitConverter.GetBytes(value[i]);
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
ushort value1 = BitConverter.ToUInt16(data, 0);
|
||
ushort value2 = BitConverter.ToUInt16(data, 2);
|
||
ushort value3 = BitConverter.ToUInt16(data, 4);
|
||
ushort value4 = BitConverter.ToUInt16(data, 6);
|
||
values.Add(value1);
|
||
values.Add(value2);
|
||
values.Add(value3);
|
||
values.Add(value4);
|
||
}
|
||
WriteDeviceBlock(address, values.ToArray());
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Int64
|
||
public long ReadInt64(string address)
|
||
{
|
||
try
|
||
{
|
||
return ReadInt64(address, 1)[0];
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public long[] ReadInt64(string address, int count = 1)
|
||
{
|
||
try
|
||
{
|
||
//地址解析
|
||
ushort[] value = ReadDeviceBlock(address, count * 4);
|
||
long[] values = new long[count];
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
byte[] data1 = BitConverter.GetBytes(value[4 * i + 0]);
|
||
byte[] data2 = BitConverter.GetBytes(value[4 * i + 1]);
|
||
byte[] data3 = BitConverter.GetBytes(value[4 * i + 2]);
|
||
byte[] data4 = BitConverter.GetBytes(value[4 * i + 3]);
|
||
List<byte> list = new List<byte>();
|
||
list.AddRange(data1);
|
||
list.AddRange(data2);
|
||
list.AddRange(data3);
|
||
list.AddRange(data4);
|
||
byte[] data = list.ToArray();
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
values[i] = BitConverter.ToInt64(data, 0);
|
||
}
|
||
return values;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
public void WriteInt64(string address, long value)
|
||
{
|
||
try
|
||
{
|
||
WriteInt64(address, new long[] { value });
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
|
||
public void WriteInt64(string address, long[] value)
|
||
{
|
||
try
|
||
{
|
||
List<ushort> values = new List<ushort>();
|
||
for (int i = 0; i < value.Length; i++)
|
||
{
|
||
byte[] data = BitConverter.GetBytes(value[i]);
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
Array.Reverse(data);
|
||
}
|
||
ushort value1 = BitConverter.ToUInt16(data, 0);
|
||
ushort value2 = BitConverter.ToUInt16(data, 2);
|
||
ushort value3 = BitConverter.ToUInt16(data, 4);
|
||
ushort value4 = BitConverter.ToUInt16(data, 6);
|
||
values.Add(value1);
|
||
values.Add(value2);
|
||
values.Add(value3);
|
||
values.Add(value4);
|
||
}
|
||
WriteDeviceBlock(address, values.ToArray());
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception(e.Message);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
}
|
||
public enum McFrame
|
||
{
|
||
MC1E = 4,
|
||
MC3E = 11,
|
||
MC4E = 15
|
||
}
|
||
|
||
/// <summary>
|
||
/// 定义 PLC 设备类型的枚举
|
||
/// </summary>
|
||
public enum PlcDeviceType
|
||
{
|
||
// PLC设备
|
||
M = 0x90
|
||
, SM = 0x91
|
||
, L = 0x92
|
||
, F = 0x93
|
||
, V = 0x94
|
||
, S = 0x98
|
||
, X = 0x9C
|
||
, Y = 0x9D
|
||
, B = 0xA0
|
||
, SB = 0xA1
|
||
, DX = 0xA2
|
||
, DY = 0xA3
|
||
, D = 0xA8
|
||
, SD = 0xA9
|
||
, R = 0xAF
|
||
, ZR = 0xB0
|
||
, W = 0xB4
|
||
, SW = 0xB5
|
||
, TC = 0xC0
|
||
, TS = 0xC1
|
||
, TN = 0xC2
|
||
, CC = 0xC3
|
||
, CS = 0xC4
|
||
, CN = 0xC5
|
||
, SC = 0xC6
|
||
, SS = 0xC7
|
||
, SN = 0xC8
|
||
, Z = 0xCC
|
||
, TT
|
||
, TM
|
||
, CT
|
||
, CM
|
||
, A
|
||
, Max
|
||
}
|
||
}
|