using HslCommunication.Core;
using HslCommunication.Core.IMessage;
using HslCommunication.Core.Net;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HslCommunication.BasicFramework;
namespace HslCommunication.Profinet.LSIS
{
///
/// XGB Fast Enet I/F module supports open Ethernet. It provides network configuration that is to connect LSIS and other company PLC, PC on network
///
///
/// Address example likes the follow
/// [welcome to finish]
///
public class XGBFastEnet : NetworkDeviceBase
{
#region Constractor
///
/// Instantiate a Default object
///
public XGBFastEnet()
{
WordLength = 2;
IpAddress = string.Empty;
Port = 2004;
}
///
/// Instantiate a object by ipaddress and port
///
/// the ip address of the plc
/// the port of the plc, default is 2004
public XGBFastEnet(string ipAddress, int port)
{
WordLength = 2;
IpAddress = ipAddress;
Port = port;
}
#endregion
#region Public Properties
///
/// CPU TYPE
///
public string CpuType { get; private set; }
///
/// Cpu is error
///
public bool CpuError { get; private set; }
///
/// RUN, STOP, ERROR, DEBUG
///
public LSCpuStatus LSCpuStatus { get; private set; }
///
/// FEnet I/F module’s Base No.
///
public byte BaseNo
{
get => baseNo;
set => baseNo = value;
}
///
/// FEnet I/F module’s Slot No.
///
public byte SlotNo
{
get => slotNo;
set => slotNo = value;
}
#endregion
#region Read Write
///
/// Read Bytes from plc, you should specify address
///
/// Start Address, for example: M100
/// Array of data Lengths
/// Whether to read the successful result object
///
///
///
///
///
public override OperateResult Read(string address, ushort length)
{
// build read command
OperateResult coreResult = BuildReadByteCommand(address, length);
if (!coreResult.IsSuccess) return coreResult;
// communication
var read = ReadFromCoreServer(PackCommand(coreResult.Content));
if (!read.IsSuccess) return OperateResult.CreateFailedResult(read);
// analysis read result
return ExtractActualData(read.Content);
}
///
/// Write bytes to plc, you should specify bytes, can't be null
///
/// Start Address, for example: M100
/// source dara
/// Whether to write the successful result object
///
public override OperateResult Write(string address, byte[] value)
{
// build write command
OperateResult coreResult = BuildWriteByteCommand(address, value);
if (!coreResult.IsSuccess) return coreResult;
// communication
var read = ReadFromCoreServer(PackCommand(coreResult.Content));
if (!read.IsSuccess) return OperateResult.CreateFailedResult(read);
// analysis read result
return ExtractActualData(read.Content);
}
#endregion
#region Read Write Byte
///
/// Read single byte value from plc
///
/// Start address
/// result
public OperateResult ReadByte(string address)
{
var read = Read(address, 1);
if (!read.IsSuccess) return OperateResult.CreateFailedResult(read);
return OperateResult.CreateSuccessResult(read.Content[0]);
}
///
/// Write single byte value to plc
///
/// Start address
/// value
/// Whether to write the successful
public OperateResult Write(string address, byte value)
{
return Write(address, new byte[] { value });
}
///
/// WriteCoil
///
///
///
///
public OperateResult WriteCoil(string address, bool value)
{
return Write(address, new byte[] { (byte)(value == true ? 0x01 : 0x00), 0x00 });
}
#endregion
#region Private Member
private byte[] PackCommand(byte[] coreCommand)
{
byte[] command = new byte[coreCommand.Length + 20];
Encoding.ASCII.GetBytes(CompanyID1).CopyTo(command, 0);
switch (cpuInfo)
{
case LSCpuInfo.XGK: command[12] = 0xA0; break;
case LSCpuInfo.XGI: command[12] = 0xA4; break;
case LSCpuInfo.XGR: command[12] = 0xA8; break;
case LSCpuInfo.XGB_MK: command[12] = 0xB0; break;
case LSCpuInfo.XGB_IEC: command[12] = 0xB4; break;
default: break;
}
command[13] = 0x33;
BitConverter.GetBytes((short)coreCommand.Length).CopyTo(command, 16);
command[18] = (byte)(baseNo * 16 + slotNo);
int count = 0;
for (int i = 0; i < 19; i++)
{
count += command[i];
}
command[19] = (byte)count;
coreCommand.CopyTo(command, 20);
string hex = SoftBasic.ByteToHexString(command, ' ');
return command;
}
#endregion
#region Const Value
private const string CompanyID1 = "LSIS-XGT";
private const string CompanyID2 = "LGIS-GLOGA";
private LSCpuInfo cpuInfo = LSCpuInfo.XGK;
private byte baseNo = 0;
private byte slotNo = 3;
#endregion
#region Static Helper
///
/// AnalysisAddress
///
///
///
///
public static OperateResult AnalysisAddress(string address, bool isRead)
{
// P,M,L,K,F,T
// P,M,L,K,F,T,C,D,S
StringBuilder sb = new StringBuilder();
try
{
sb.Append("%");
char[] types = new char[] { 'P', 'M', 'L', 'K', 'F', 'T', 'C', 'D', 'S', 'Q', 'I', 'N', 'U', 'Z', 'R' };
bool exsist = false;
int baseAddress;
int LsBaseNumber = 0;
int LsSlotNumber = 0;
string fullAddress = address;
if (isRead)
{
for (int i = 0; i < types.Length; i++)
{
if (types[i] == address[0])
{
sb.Append(types[i]);
sb.Append("B");
if (address[1] == 'X')//Bit
{
if (address.IndexOf(".") > 1) //MX0.0
{
string[] list = address.Split('.');
baseAddress = ((int.Parse($"{list[2]}") >= 16) ? (int.Parse($"{list[2]}") / 16) : 0) * 2;
sb.Append(baseAddress);
}
else
{
sb.Append(int.Parse($"{address[2]}"));
}
}
else if (address[1] == 'B')//BitOnByte
{
if (address.IndexOf(".") > 0)
{
string[] list = address.Split('.');
for (int y = 0; y < list.Length; y++)
{
if (list[y][0] == 'I' || list[y][0] == 'Q' || list[y][0] == 'U') //IB0.0.0.1
{
baseAddress = ((int.Parse($"{list[3]}") >= 2) ? (int.Parse($"{list[3]}") / 2) : 0) * 2;
sb.Append(baseAddress);
break;
}
else
{
sb.Append(int.Parse($"{list[1]}")); //MB0.0
break;
}
}
}
else
{
sb.Append(int.Parse($"{address[2]}"));
}
}
else if (address[1] == 'W')
{
if (address.IndexOf(".") > 0)//BitOnWord
{
string[] list = address.Split('.');
for (int y = 0; y < list.Length; y++)
{
if (list[y][0] == 'I' || list[y][0] == 'Q' || list[y][0] == 'U')//IW0.0.0.1
{
baseAddress = ((int.Parse($"{list[3]}") >= 2) ? (int.Parse($"{list[3]}") / 2) : 0) * 2;
sb.Append(baseAddress);
break;
}
else
{
sb.Append(int.Parse($"{list[1]}") * 2);//MW0.0
break;
}
}
}
else
{
sb.Append(int.Parse($"{address[2]}") * 2);//MW0
}
}
else if (address[1] == 'D')
{
sb.Append(int.Parse($"{address[2]}") * 4);
}
else if (address[1] == 'L')
{
sb.Append(int.Parse($"{address[2]}") * 8);
}
else
{
sb.Append(int.Parse($"{address[1]}"));
}
exsist = true;
break;
}
}
}
else
{
if (address.Length >= 3 || address.Length >= 5)
{
for (int i = 0; i < types.Length; i++)
{
if (types[i] == address[0])
{
if (address[1] == 'B')
{
if (address.IndexOf(".") > 0)//BitOnByte
{
string text3 = address.Substring(0, 1);
address.Substring(1, 1);
string text4 = address.Remove(0, 2);
switch (text3)
{
case "I":
case "Q":
case "U":
{
string[] array4 = text4.Split(new char[1]
{
'.'
}, 4);
fullAddress = text3 + "B" + array4[0] + "." + array4[1] + ".";
LsBaseNumber = int.Parse(array4[2]);
LsSlotNumber = int.Parse(array4[3]);
break;
}
default:
{
string[] array3 = text4.Split(new char[1]
{
'.'
}, 2);
fullAddress = text3 + "B";
LsBaseNumber = int.Parse(array3[0]);
LsSlotNumber = int.Parse(array3[1]);
break;
}
}
fullAddress = $"{fullAddress}{(LsBaseNumber * 8 + LsSlotNumber) / 8}.{LsSlotNumber % 8}";
sb.Append(fullAddress);
}
else
{
sb.Append(address);
}
exsist = true;
break;
}
else if (address[1] == 'W')
{
if (address.IndexOf(".") > 0)//BitOnWord
{
string text = address.Substring(0, 1);
address.Substring(1, 1);
string text2 = address.Remove(0, 2);
switch (text)
{
case "I":
case "Q":
case "U":
{
string[] array2 = text2.Split(new char[1]
{
'.'
}, 4);
fullAddress = text + "B" + array2[0] + "." + array2[1] + ".";
LsBaseNumber = int.Parse(array2[2]);
LsSlotNumber = int.Parse(array2[3]);
break;
}
default:
{
string[] array = text2.Split(new char[1]
{
'.'
}, 2);
fullAddress = text + "B";
LsBaseNumber = int.Parse(array[0]);
LsSlotNumber = int.Parse(array[1]);
break;
}
}
fullAddress = $"{fullAddress}{(LsBaseNumber * 16 + LsSlotNumber) / 8}.{LsSlotNumber % 8}";
}
else
{
sb.Append(fullAddress);
}
exsist = true;
break;
}
else if (address[1] == 'D')
{
sb.Append(address);
exsist = true;
break;
}
else if (address[1] == 'X')
{
sb.Append(address);
exsist = true;
break;
}
else
{
exsist = false;
break;
}
}
}
}
}
if (!exsist) throw new Exception(StringResources.Language.NotSupportedDataType);
}
catch (Exception ex)
{
return new OperateResult(ex.Message);
}
return OperateResult.CreateSuccessResult(sb.ToString());
}
///
/// Get DataType to Address
///
///
///
public static OperateResult GetDataTypeToAddress(string address)
{
string lSDataType = string.Empty; ;
try
{
char[] types = new char[] { 'P', 'M', 'L', 'K', 'F', 'T', 'C', 'D', 'S', 'Q', 'I', 'R' };
bool exsist = false;
for (int i = 0; i < types.Length; i++)
{
if (types[i] == address[0])
{
if (address[1] == 'W')
{
lSDataType = "Word";
}
else if (address[1] == 'D')
{
lSDataType = "DWord";
}
else if (address[1] == 'L')
{
lSDataType = "LWord";
}
else if (address[1] == 'B')
{
lSDataType = "Byte";
}
else if (address[1] == 'X')
{
lSDataType = "Bit";
}
else
{
exsist = false;
break;
}
exsist = true;
break;
}
}
if (!exsist) throw new Exception(StringResources.Language.NotSupportedDataType);
}
catch (Exception ex)
{
return new OperateResult(ex.Message);
}
return OperateResult.CreateSuccessResult(lSDataType);
}
private static OperateResult BuildReadByteCommand(string address, ushort length)
{
var analysisResult = AnalysisAddress(address, true);
if (!analysisResult.IsSuccess) return OperateResult.CreateFailedResult(analysisResult);
byte[] command = new byte[12 + analysisResult.Content.Length];
command[0] = 0x54; // read
command[1] = 0x00;
command[2] = 0x14; // continuous reading
command[3] = 0x00;
command[4] = 0x00; // Reserved
command[5] = 0x00;
command[6] = 0x01; // Block No ?? i don't know what is the meaning
command[7] = 0x00;
command[8] = (byte)analysisResult.Content.Length; // Variable Length
command[9] = 0x00;
Encoding.ASCII.GetBytes(analysisResult.Content).CopyTo(command, 10);
BitConverter.GetBytes(length).CopyTo(command, command.Length - 2);
return OperateResult.CreateSuccessResult(command);
}
private static OperateResult BuildWriteByteCommand(string address, byte[] data)
{
var analysisResult = AnalysisAddress(address, false);
if (!analysisResult.IsSuccess) return OperateResult.CreateFailedResult(analysisResult);
var DataTypeResult = GetDataTypeToAddress(address);
if (!DataTypeResult.IsSuccess) return OperateResult.CreateFailedResult(DataTypeResult);
byte[] command = new byte[12 + analysisResult.Content.Length + data.Length];
switch (DataTypeResult.Content)
{
case "Bit":
command[2] = 0x00; break;
case "Byte":
command[2] = 0x01; break;
case "Word":
command[2] = 0x02; break;
case "DWord": command[2] = 0x03; break;
case "LWord": command[2] = 0x04; break;
case "Continuous": command[2] = 0x14; break;
default: break;
}
command[0] = 0x58; // write
command[1] = 0x00;
//command[2] = 0x14; // continuous reading
command[3] = 0x00;
command[4] = 0x00; // Reserved
command[5] = 0x00;
command[6] = 0x01; // Block No ?? i don't know what is the meaning
command[7] = 0x00;
command[8] = (byte)analysisResult.Content.Length; // Variable Length
command[9] = 0x00;
Encoding.ASCII.GetBytes(analysisResult.Content).CopyTo(command, 10);
BitConverter.GetBytes(data.Length).CopyTo(command, command.Length - 2 - data.Length);
data.CopyTo(command, command.Length - data.Length);
return OperateResult.CreateSuccessResult(command);
}
///
/// Returns true data content, supports read and write returns
///
/// response data
/// real data
public OperateResult ExtractActualData(byte[] response)
{
if (response.Length < 20) return new OperateResult("Length is less than 20:" + SoftBasic.ByteToHexString(response));
ushort plcInfo = BitConverter.ToUInt16(response, 10);
BitArray array_plcInfo = new BitArray(BitConverter.GetBytes(plcInfo));
switch (plcInfo % 32)
{
case 1: CpuType = "XGK/R-CPUH"; break;
case 2: CpuType = "XGK-CPUS"; break;
case 5: CpuType = "XGK/R-CPUH"; break;
}
CpuError = array_plcInfo[7];
if (array_plcInfo[8]) LSCpuStatus = LSCpuStatus.RUN;
if (array_plcInfo[9]) LSCpuStatus = LSCpuStatus.STOP;
if (array_plcInfo[10]) LSCpuStatus = LSCpuStatus.ERROR;
if (array_plcInfo[11]) LSCpuStatus = LSCpuStatus.DEBUG;
if (response.Length < 28) return new OperateResult("Length is less than 28:" + SoftBasic.ByteToHexString(response));
ushort error = BitConverter.ToUInt16(response, 26);
if (error > 0) return new OperateResult(response[28], "Error:" + GetErrorDesciption(response[28]));
if (response[20] == 0x59) return OperateResult.CreateSuccessResult(new byte[0]); // write
if (response[20] == 0x55) // read
{
try
{
ushort length = BitConverter.ToUInt16(response, 30);
byte[] content = new byte[length];
Array.Copy(response, 32, content, 0, length);
return OperateResult.CreateSuccessResult(content);
}
catch (Exception ex)
{
return new OperateResult(ex.Message);
}
}
return new OperateResult(StringResources.Language.NotSupportedFunction);
}
///
/// get the description of the error code meanning
///
/// code value
/// string information
public static string GetErrorDesciption(byte code)
{
switch (code)
{
case 0: return "Normal";
case 1: return "Physical layer error (TX, RX unavailable)";
case 3: return "There is no identifier of Function Block to receive in communication channel";
case 4: return "Mismatch of data type";
case 5: return "Reset is received from partner station";
case 6: return "Communication instruction of partner station is not ready status";
case 7: return "Device status of remote station is not desirable status";
case 8: return "Access to some target is not available";
case 9: return "Can’ t deal with communication instruction of partner station by too many reception";
case 10: return "Time Out error";
case 11: return "Structure error";
case 12: return "Abort";
case 13: return "Reject(local/remote)";
case 14: return "Communication channel establishment error (Connect/Disconnect)";
case 15: return "High speed communication and connection service error";
case 33: return "Can’t find variable identifier";
case 34: return "Address error";
case 50: return "Response error";
case 113: return "Object Access Unsupported";
case 187: return "Unknown error code (communication code of other company) is received";
default: return "Unknown error";
}
}
#endregion
#region Override
///
/// Returns a string representing the current object
///
/// 字符串
public override string ToString()
{
return $"XGBFastEnet[{IpAddress}:{Port}]";
}
#endregion
}
}