using Microsoft.Extensions.Logging; using System; using System.Threading.Tasks; namespace Modbus.Net.Siemens { /// /// 西门子协议类型 /// public enum SiemensType { /// /// PPI /// Ppi = 0, #pragma warning disable /// /// MPI /// //Mpi = 1, #pragma warning restore /// /// 以太网 /// Tcp = 2 } /// /// 西门子设备类型 /// public enum SiemensMachineModel { /// /// S7-200 /// S7_200 = 0, /// /// S7-200 Smart /// S7_200_Smart = 1, /// /// S7-300 /// S7_300 = 2, /// /// S7-400 /// S7_400 = 3, /// /// S7-1200 /// S7_1200 = 4, /// /// S7-1500 /// S7_1500 = 5 } /// /// 西门子通讯Api入口 /// public class SiemensUtility : BaseUtility, PipeUnit> { private static readonly ILogger logger = LogProvider.CreateLogger(); private readonly ushort _maxCalled; private readonly ushort _maxCalling; private readonly ushort _maxPdu; private readonly ushort _taspSrc; private readonly byte _tdpuSize; private readonly ushort _tsapDst; private ushort _sendCount; private readonly object _counterLock = new object(); private SiemensType _siemensType; /// /// 构造函数 /// /// 连接类型 /// 连接字符串 /// 设备类型 /// 从站地址 /// 主站地址 /// 本机模块位,0到7,200为本地栈号,比如10.01则填写0x01 /// PLC模块位,0到7,200为远程栈号,比如10.02则填写0x02 /// 300和400为槽号机架号,机架号为1,比如槽号为3,则填写0x13 public SiemensUtility(SiemensType connectionType, string connectionString, SiemensMachineModel model, byte slaveAddress, byte masterAddress, byte src = 0, byte dst = 1) : base(slaveAddress, masterAddress) { ConnectionString = connectionString; switch (model) { case SiemensMachineModel.S7_200: { _tdpuSize = 0x09; _taspSrc = (ushort)(0x1000 + src); _tsapDst = (ushort)(0x1000 + dst); _maxCalling = 0x0001; _maxCalled = 0x0001; _maxPdu = 0x03c0; break; } case SiemensMachineModel.S7_300: case SiemensMachineModel.S7_400: { _tdpuSize = 0x1a; _taspSrc = 0x4b54; _tsapDst = (ushort)(0x0300 + dst); _maxCalling = 0x0001; _maxCalled = 0x0001; _maxPdu = 0x00f0; break; } case SiemensMachineModel.S7_1200: case SiemensMachineModel.S7_1500: { _tdpuSize = 0x0a; _taspSrc = 0x1011; _tsapDst = (ushort)(0x0300 + dst); _maxCalling = 0x0003; _maxCalled = 0x0003; _maxPdu = 0x0100; break; } case SiemensMachineModel.S7_200_Smart: { _tdpuSize = 0x0a; _taspSrc = 0x0101; _tsapDst = 0x0101; _maxCalling = 0x0001; _maxCalled = 0x0001; _maxPdu = 0x03c0; break; } default: { throw new NotImplementedException("Siemens PLC Model not Supported"); } } ConnectionType = connectionType; AddressTranslator = new AddressTranslatorSiemens(); _sendCount = 0; } /// /// 端格式 /// public override Endian Endian => Endian.BigEndianLsb; /// /// IP地址 /// protected string ConnectionStringIp { get { if (ConnectionString == null) return null; return ConnectionString.Contains(":") ? ConnectionString.Split(':')[0] : ConnectionString; } } /// /// 端口 /// protected int? ConnectionStringPort { get { if (ConnectionString == null) return null; if (!ConnectionString.Contains(":")) return null; var connectionStringSplit = ConnectionString.Split(':'); try { return connectionStringSplit.Length < 2 ? (int?)null : int.Parse(connectionStringSplit[1]); } catch (Exception e) { logger.LogError(e, $"SiemensUtility: {ConnectionString} format error"); return null; } } } /// /// 西门子连接类型 /// public SiemensType ConnectionType { get { return _siemensType; } set { _siemensType = value; switch (_siemensType) { //PPI case SiemensType.Ppi: { Wrapper = ConnectionString == null ? new SiemensPpiProtocol(SlaveAddress, MasterAddress) : new SiemensPpiProtocol(ConnectionString, SlaveAddress, MasterAddress); break; } //MPI //case SiemensType.Mpi: //{ //throw new NotImplementedException(); //} //Ethenet case SiemensType.Tcp: { Wrapper = ConnectionString == null ? new SiemensTcpProtocol(_tdpuSize, _taspSrc, _tsapDst, _maxCalling, _maxCalled, _maxPdu) : (ConnectionStringPort == null ? new SiemensTcpProtocol(_tdpuSize, _taspSrc, _tsapDst, _maxCalling, _maxCalled, _maxPdu, ConnectionString) : new SiemensTcpProtocol(_tdpuSize, _taspSrc, _tsapDst, _maxCalling, _maxCalled, _maxPdu, ConnectionStringIp, ConnectionStringPort.Value)); break; } } } } /// /// 设置连接类型 /// /// 需要设置的连接类型 public override void SetConnectionType(int connectionType) { ConnectionType = (SiemensType)connectionType; } /// /// 读数据 /// /// 开始地址 /// 读取字节个数 /// 读取原始个数 /// 从设备中读取的数据 public override async Task> GetDatasAsync(string startAddress, int getByteCount, int getOriginalCount) { try { ReadRequestSiemensInputStruct readRequestSiemensInputStruct; lock (_counterLock) { _sendCount = (ushort)(_sendCount % ushort.MaxValue + 1); readRequestSiemensInputStruct = new ReadRequestSiemensInputStruct(SlaveAddress, MasterAddress, _sendCount, SiemensTypeCode.Byte, startAddress, (ushort)getByteCount, AddressTranslator); } var readRequestSiemensOutputStruct = await Wrapper.SendReceiveAsync( Wrapper[typeof(ReadRequestSiemensProtocol)], readRequestSiemensInputStruct); return new ReturnStruct { Datas = readRequestSiemensOutputStruct?.GetValue, IsSuccess = true, ErrorCode = 0, ErrorMsg = "" }; } catch (SiemensProtocolErrorException e) { logger.LogError(e, $"SiemensUtility -> GetDatas: {ConnectionString} error: {e.Message}"); return new ReturnStruct { Datas = null, IsSuccess = false, ErrorCode = e.ErrorCode, ErrorMsg = e.Message }; } catch (FormatException e) { logger.LogError(e, $"SiemensUtility -> GetDatas: {ConnectionString} error: {e.Message}"); return new ReturnStruct { Datas = null, IsSuccess = false, ErrorCode = -1, ErrorMsg = e.Message }; } } /// /// 写数据 /// /// 开始地址 /// 需要写入的数据 /// 写入数据的原始长度 /// 写入是否成功 public override async Task> SetDatasAsync(string startAddress, object[] setContents, int setOriginalCount) { try { WriteRequestSiemensInputStruct writeRequestSiemensInputStruct; lock (_counterLock) { _sendCount = (ushort)(_sendCount % ushort.MaxValue + 1); writeRequestSiemensInputStruct = new WriteRequestSiemensInputStruct(SlaveAddress, MasterAddress, _sendCount, startAddress, setContents, AddressTranslator); } var writeRequestSiemensOutputStruct = await Wrapper.SendReceiveAsync( Wrapper[typeof(WriteRequestSiemensProtocol)], writeRequestSiemensInputStruct); return new ReturnStruct { Datas = writeRequestSiemensOutputStruct?.AccessResult == SiemensAccessResult.NoError, IsSuccess = writeRequestSiemensOutputStruct?.AccessResult == SiemensAccessResult.NoError, ErrorCode = writeRequestSiemensOutputStruct?.AccessResult == SiemensAccessResult.NoError ? 0 : (int)writeRequestSiemensOutputStruct?.AccessResult, ErrorMsg = writeRequestSiemensOutputStruct?.AccessResult.ToString() }; } catch (SiemensProtocolErrorException e) { logger.LogError(e, $"ModbusUtility -> SetDatas: {ConnectionString} error: {e.Message}"); return new ReturnStruct { Datas = false, IsSuccess = false, ErrorCode = e.ErrorCode, ErrorMsg = e.Message }; } catch (FormatException e) { logger.LogError(e, $"SiemensUtility -> GetDatas: {ConnectionString} error: {e.Message}"); return new ReturnStruct { Datas = false, IsSuccess = false, ErrorCode = -1, ErrorMsg = e.Message }; } } } }