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