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