| #region << 版 本 注 释 >> | 
| /*---------------------------------------------------------------- | 
|  * 命名空间:WIDESEAWCS_Communicator | 
|  * 创建者:胡童庆 | 
|  * 创建时间:2024/8/2 16:13:36 | 
|  * 版本:V1.0.0 | 
|  * 描述:西门子S7通讯类的封装,继承BaseCommunicator抽象类 | 
|  * | 
|  * ---------------------------------------------------------------- | 
|  * 修改人: | 
|  * 修改时间: | 
|  * 版本:V1.0.1 | 
|  * 修改说明: | 
|  *  | 
|  *----------------------------------------------------------------*/ | 
| #endregion << 版 本 注 释 >> | 
|   | 
| using HslCommunication; | 
| using HslCommunication.Core; | 
| using HslCommunication.LogNet; | 
| using HslCommunication.Profinet.Siemens; | 
| using Newtonsoft.Json; | 
| using System; | 
| using System.Collections; | 
| using System.Collections.Generic; | 
| using System.ComponentModel; | 
| using System.Diagnostics.CodeAnalysis; | 
| using System.Linq; | 
| using System.Net; | 
| using System.Net.NetworkInformation; | 
| using System.Reflection; | 
| using System.Text; | 
| using System.Threading.Tasks; | 
| using System.Xml.Linq; | 
|   | 
| namespace WIDESEAWCS_Communicator | 
| { | 
|     /// <summary> | 
|     /// 西门子S7通讯类 | 
|     /// </summary> | 
|     [Description("西门子S7")] | 
|     public class SiemensS7 : BaseCommunicator | 
|     { | 
|         #region Private Member | 
|         /// <summary> | 
|         /// HSLCommunication的西门子的S7协议的通讯类 | 
|         /// </summary> | 
|         private SiemensS7Net plc; | 
|   | 
|         /// <summary> | 
|         /// 设备的IP地址。 | 
|         /// </summary> | 
|         private string _ipAddress; | 
|   | 
|         /// <summary> | 
|         /// 连接使用的端口号。 | 
|         /// </summary> | 
|         private int _port; | 
|   | 
|         /// <summary> | 
|         /// 当前通讯器是否已连接到PLC。   | 
|         /// </summary> | 
|         private bool _connected; | 
|   | 
|         /// <summary> | 
|         /// PLC名称 | 
|         /// </summary> | 
|         private string _name; | 
|   | 
|         private ILogNet _logNet; | 
|   | 
|         private bool _isPing = true; | 
|         #endregion Private Member | 
|   | 
|         #region Public Member | 
|         /// <summary>   | 
|         /// 获取当前通讯器是否已连接到PLC。   | 
|         /// </summary> | 
|         public override bool IsConnected => _connected; | 
|   | 
|         /// <summary> | 
|         /// PLC名称 | 
|         /// </summary> | 
|         public override string Name => _name; | 
|   | 
|         public override ILogNet LogNet => _logNet; | 
|         #endregion Public Member | 
|   | 
|         #region Constructor Function | 
|         /// <summary> | 
|         /// 构造函数 | 
|         /// </summary> | 
|         /// <param name="ipAddress">设备的IP地址</param> | 
|         /// <param name="port">连接使用的端口号</param> | 
|         /// <param name="name">设备名称</param> | 
|         public SiemensS7(string ipAddress, int port, string name) | 
|         { | 
|             string path = AppDomain.CurrentDomain.BaseDirectory + $"Log_PLCReadWrite\\{name}"; | 
|             _logNet = new LogNetFileSize(path, 10 * 1024 * 1024, 100); | 
|   | 
|             bool ipCheck = IPAddress.TryParse(ipAddress, out IPAddress? address); | 
|             if (!ipCheck) | 
|             { | 
|                 _logNet.WriteError(name, string.Format(CommunicationExceptionMessage.IpAddressErrorException, ipAddress)); | 
|                 throw new CommunicationException(string.Format(CommunicationExceptionMessage.IpAddressErrorException, ipAddress), CommunicationErrorType.IpAddressError); | 
|             } | 
|   | 
|             _ipAddress = ipAddress;//通过构造函数赋值设备的IP地址 | 
|             _port = port;//通过构造函数赋值连接使用的端口号 | 
|             _name = name; | 
|         } | 
|         #endregion | 
|   | 
|         #region Private Method | 
|         /// <summary> | 
|         /// 从OperateResult对象中获取读取的数据。 | 
|         /// </summary> | 
|         /// <typeparam name="T">读取的数据类型。</typeparam> | 
|         /// <param name="operateResult">HSLCommunication读取的OperateResult<T>对象</param> | 
|         /// <returns>如果读取成功,返回读取结果,读取失败,抛出自定义通讯异常</returns> | 
|         /// <exception cref="CommunicationException">自定义通讯异常类</exception> | 
|         private object GetContent<T>(OperateResult<T> operateResult, string address) | 
|         { | 
|             try | 
|             { | 
|                 if (!operateResult.IsSuccess) | 
|                 { | 
|                     throw new CommunicationException(string.Format(CommunicationExceptionMessage.ReadFailedException, typeof(T).Name, address, operateResult.Message), CommunicationErrorType.ReadFailed); | 
|                 } | 
|                 return operateResult.Content ?? throw new CommunicationException(string.Format(CommunicationExceptionMessage.ReadDataIsNull, address), CommunicationErrorType.ReadFailed); | 
|             } | 
|             catch (Exception ex) | 
|             { | 
|                 LogNet.WriteException(Name, ex.Message, ex); | 
|                 throw new CommunicationException(ex.Message, CommunicationErrorType.ReadFailed, innerException: ex); | 
|             } | 
|         } | 
|   | 
|         /// <summary> | 
|         ///  | 
|         /// </summary> | 
|         /// <typeparam name="T"></typeparam> | 
|         /// <param name="operateResult"></param> | 
|         /// <param name="address"></param> | 
|         /// <param name="value"></param> | 
|         /// <returns></returns> | 
|         /// <exception cref="CommunicationException"></exception> | 
|         private bool GetResult<T>(OperateResult operateResult, string address, T value) where T : notnull | 
|         { | 
|             StringBuilder stringBuilder = new StringBuilder(); | 
|             try | 
|             { | 
|                 stringBuilder.AppendLine(string.Format(CommunicationInfoMessage.WriteData, address, value)); | 
|                 if (!operateResult.IsSuccess) | 
|                 { | 
|                     throw new CommunicationException(string.Format(CommunicationExceptionMessage.WriteFailedException, typeof(T).Name, address, value, operateResult.Message), CommunicationErrorType.WriteFailed); | 
|                 } | 
|                 else | 
|                 { | 
|                     object? obj = null; | 
|                     for (int i = 0; i < 5; i++) | 
|                     { | 
|                         T readValue = Read<T>(address); | 
|                         stringBuilder.AppendLine(string.Format(CommunicationInfoMessage.WriteAfterRead, readValue, value)); | 
|                         obj = readValue; | 
|                         if (readValue.Equals(value)) | 
|                         { | 
|                             stringBuilder.AppendLine(string.Format(CommunicationInfoMessage.WriteAndReadCheckSuccess, address, value, readValue)); | 
|                             return true; | 
|                         } | 
|                         else if (i < 4) | 
|                         { | 
|                             Write(address, value); | 
|                         } | 
|                     } | 
|                     stringBuilder.AppendLine(string.Format(CommunicationExceptionMessage.WriteAndReadCheckFaild, address, value, obj)); | 
|                     throw new CommunicationException(stringBuilder.ToString(), CommunicationErrorType.WriteFailed); | 
|                 } | 
|             } | 
|             catch (Exception ex) | 
|             { | 
|                 LogNet.WriteException(Name, ex.Message, ex); | 
|                 throw new CommunicationException(ex.Message, CommunicationErrorType.WriteFailed, innerException: ex); | 
|             } | 
|             finally | 
|             { | 
|                 LogNet.WriteInfo(Name, stringBuilder.ToString()); | 
|             } | 
|   | 
|         } | 
|   | 
|         /// <summary> | 
|         /// 写入数据 | 
|         /// </summary> | 
|         /// <param name="address"></param> | 
|         /// <param name="value"></param> | 
|         /// <returns></returns> | 
|         /// <exception cref="CommunicationException"></exception> | 
|         private OperateResult Write(string address, object value) | 
|         { | 
|             try | 
|             { | 
|                 Type type = value.GetType(); | 
|   | 
|                 switch (Type.GetTypeCode(type)) | 
|                 { | 
|                     case TypeCode.Int32: | 
|                         return plc.Write(address, Convert.ToInt32(value)); | 
|                     case TypeCode.UInt32: | 
|                         return plc.Write(address, Convert.ToUInt32(value)); | 
|                     case TypeCode.Int16: | 
|                         return plc.Write(address, Convert.ToInt16(value)); | 
|                     case TypeCode.UInt16: | 
|                         return plc.Write(address, Convert.ToUInt16(value)); | 
|                     case TypeCode.Single: | 
|                         return plc.Write(address, Convert.ToSingle(value)); | 
|                     case TypeCode.Boolean: | 
|                         return plc.Write(address, Convert.ToBoolean(value)); | 
|                     case TypeCode.Byte: | 
|                         return plc.Write(address, Convert.ToByte(value)); | 
|                     case TypeCode.String: | 
|                         return plc.Write(address, Convert.ToString(value)); | 
|                     case TypeCode.Char: | 
|                         return plc.Write(address, Convert.ToChar(value)); | 
|                     default: | 
|                         throw new CommunicationException(string.Format(CommunicationExceptionMessage.DataTypeErrorException, type.Name, address), CommunicationErrorType.TypeError); | 
|                 } | 
|             } | 
|             catch (CommunicationException ex) | 
|             { | 
|                 throw new CommunicationException(ex.Message, ex.ErrorType); | 
|             } | 
|             catch (Exception ex) | 
|             { | 
|                 //读取异常时抛出自定义通讯异常类 | 
|                 throw new CommunicationException(string.Format(CommunicationExceptionMessage.DataTypeErrorException, address, value), CommunicationErrorType.TypeError, innerException: ex); | 
|             } | 
|         } | 
|   | 
|         private object Read(string address, TypeCode typeCode) | 
|         { | 
|             try | 
|             { | 
|                 switch (typeCode) | 
|                 { | 
|                     case TypeCode.Int32: | 
|                         return (int)GetContent(plc.ReadInt32(address), address); | 
|                     case TypeCode.UInt32: | 
|                         return (uint)GetContent(plc.ReadUInt32(address), address); | 
|                     case TypeCode.Int16: | 
|                         return (short)GetContent(plc.ReadInt16(address), address); | 
|                     case TypeCode.UInt16: | 
|                         return (ushort)GetContent(plc.ReadUInt16(address), address); | 
|                     case TypeCode.Single: | 
|                         return (float)GetContent(plc.ReadFloat(address), address); | 
|                     case TypeCode.Boolean: | 
|                         return (bool)GetContent(plc.ReadBool(address), address); | 
|                     case TypeCode.Byte: | 
|                         return (byte)GetContent(plc.ReadByte(address), address); | 
|                     case TypeCode.String: | 
|                         return (string)GetContent(plc.ReadString(address), address); | 
|                     case TypeCode.Char: | 
|                         return (char)GetContent(plc.ReadByte(address), address); | 
|                     default: | 
|                         throw new CommunicationException(string.Format(CommunicationExceptionMessage.DataTypeErrorException, typeCode.ToString(), address), CommunicationErrorType.TypeError); | 
|                 } | 
|             } | 
|             catch (CommunicationException ex) | 
|             { | 
|                 //读取异常时抛出自定义通讯异常类 | 
|                 throw new CommunicationException(ex.Message, ex.ErrorType); | 
|             } | 
|             catch (Exception ex) | 
|             { | 
|                 //读取异常时抛出自定义通讯异常类 | 
|                 throw new CommunicationException($"读取数据异常,错误信息:{ex.Message}", CommunicationErrorType.ReadException, innerException: ex); | 
|             } | 
|         } | 
|   | 
|         private void Ping() | 
|         { | 
|             Task.Run(() => | 
|             { | 
|                 while (_isPing) | 
|                 { | 
|                     try | 
|                     { | 
|                         IPStatus status = plc.IpAddressPing(); | 
|                         if (status == IPStatus.Success) | 
|                             _connected = true; | 
|                         else | 
|                             _connected = false; | 
|                     } | 
|                     finally | 
|                     { | 
|                         Thread.Sleep(100); | 
|                     } | 
|                 } | 
|             }); | 
|   | 
|         } | 
|         #endregion | 
|   | 
|         #region Public Method | 
|         /// <summary>   | 
|         /// 连接到PLC。 | 
|         /// </summary>   | 
|         /// <returns>如果连接成功则返回true,否则返回false。</returns> | 
|         /// <exception cref="CommunicationException">自定义通讯异常类</exception> | 
|         public override bool Connect() | 
|         { | 
|             try | 
|             { | 
|   | 
|                 //实例化一个西门子的S7协议的通讯对象 | 
|                 plc = new SiemensS7Net(SiemensPLCS.S1500) | 
|                 { | 
|                     IpAddress = _ipAddress, | 
|                     Port = _port | 
|                 }; | 
|                 OperateResult operateResult = plc.ConnectServer();//连接PLC | 
|                 _connected = operateResult.IsSuccess;//将连接是否成功赋值给当前通讯器是否已连接到PLC | 
|   | 
|                 if (_connected) | 
|                     LogNet.WriteInfo(Name, string.Format(CommunicationInfoMessage.ConnectSuccess, _ipAddress, _port)); | 
|                 else | 
|                     LogNet.WriteError(Name, string.Format(CommunicationExceptionMessage.ConnectFaild, _ipAddress, _port, operateResult.Message)); | 
|                 Ping(); | 
|                 return operateResult.IsSuccess; | 
|             } | 
|             catch (Exception ex) | 
|             { | 
|                 LogNet.WriteException(Name, string.Format(CommunicationExceptionMessage.ConnectFaild, _ipAddress, _port, ex.Message), ex); | 
|                 //连接异常时抛出自定义异常类 | 
|                 throw new CommunicationException(ex.Message, CommunicationErrorType.ConnectionFailed, innerException: ex); | 
|             } | 
|         } | 
|   | 
|         /// <summary>   | 
|         /// 断开与工业设备的连接。   | 
|         /// </summary>   | 
|         /// <returns>如果成功断开连接则返回true,如果已经是断开状态则返回false。</returns>   | 
|         public override bool Disconnect() | 
|         { | 
|             try | 
|             { | 
|                 if (plc != null) | 
|                 { | 
|                     OperateResult operateResult = plc.ConnectClose();//断开与PLC的连接 | 
|                     return operateResult.IsSuccess; | 
|                 } | 
|                 return false; | 
|             } | 
|             catch (Exception ex) | 
|             { | 
|                 return false; | 
|             } | 
|             finally | 
|             { | 
|                 _connected = false; | 
|             } | 
|         } | 
|   | 
|         #region Read | 
|         /// <summary>   | 
|         /// 从PLC读取数据。   | 
|         /// </summary>   | 
|         /// <param name="address">源地址,具体格式取决于使用的工业协议。</param>   | 
|         /// <param name="length">要读取的数据长度。</param>   | 
|         /// <returns>读取到的数据,如果读取失败则可能返回null、空数组或抛出自定义通讯异常。</returns>   | 
|         /// <exception cref="CommunicationException">自定义通讯异常类</exception> | 
|         public override byte[] Read(string address, int length) | 
|         { | 
|             return (byte[])GetContent(plc.Read(address, (ushort)length), address); | 
|         } | 
|   | 
|         /// <summary> | 
|         /// 从PLC读取数据。 | 
|         /// </summary> | 
|         /// <typeparam name="T">读取的数据类型。</typeparam> | 
|         /// <param name="address">源地址,具体格式取决于使用的工业协议。</param> | 
|         /// <param name="length">要读取的数据长度(可选,默认值为1)。</param> | 
|         /// <returns>如果读取成功,返回读取的结果,失败则抛出自定义通讯异常</returns> | 
|         /// <exception cref="CommunicationException">自定义通讯异常类</exception> | 
|         public override T Read<T>(string address) | 
|         { | 
|             Type type = typeof(T); | 
|             return (T)Read(address, Type.GetTypeCode(type)); | 
|         } | 
|   | 
|         /// <summary> | 
|         /// 从PLC读取数据返回object。 | 
|         /// </summary> | 
|         /// <param name="address">源地址,具体格式取决于使用的工业协议。</param> | 
|         /// <param name="dataType">读取的数据类型。</param> | 
|         /// <returns>如果读取成功,返回读取的结果,失败则抛出自定义通讯异常</returns> | 
|         /// <exception cref="CommunicationException">自定义通讯异常类</exception> | 
|         public override object ReadAsObj(string address, string dataType) | 
|         { | 
|             return Read(address, SiemensDBDataType.GetTypeCode(dataType)); | 
|         } | 
|         #endregion | 
|   | 
|         #region Write | 
|         /// <summary> | 
|         /// 向PLC写入数据。 | 
|         /// </summary> | 
|         /// <param name="address">源地址,具体格式取决于使用的工业协议。</param> | 
|         /// <param name="data">要写入的数据。</param> | 
|         /// <returns>如果写入成功则返回true,如果写入失败则可能返回false或抛出自定义通讯异常。</returns> | 
|         /// <exception cref="CommunicationException">自定义通讯异常类</exception> | 
|         public override bool Write(string address, byte[] data) | 
|         { | 
|             try | 
|             { | 
|                 OperateResult result = plc.Write(address, data); | 
|                 if (result.IsSuccess) | 
|                 { | 
|                     return result.IsSuccess; | 
|                 } | 
|                 else | 
|                 { | 
|                     //todo 写入失败 | 
|                     return false; | 
|                 } | 
|             } | 
|             catch (Exception ex) | 
|             { | 
|                 //写入异常时抛出自定义通讯异常类 | 
|                 throw new CommunicationException($"写入数据异常,地址:【{address}】,错误信息: {ex.Message}", CommunicationErrorType.ReadFailed, innerException: ex); | 
|             } | 
|         } | 
|   | 
|         /// <summary> | 
|         ///  | 
|         /// </summary> | 
|         /// <typeparam name="T"></typeparam> | 
|         /// <param name="address"></param> | 
|         /// <param name="value"></param> | 
|         /// <returns></returns> | 
|         /// <exception cref="NotImplementedException"></exception> | 
|         public override bool Write<T>(string address, T value) | 
|         { | 
|             return GetResult(Write(address, value), address, value); | 
|         } | 
|   | 
|         /// <summary> | 
|         /// 向PLC写入数据。 | 
|         /// </summary> | 
|         /// <param name="address">源地址,具体格式取决于使用的工业协议。</param> | 
|         /// <param name="dataType">要写入的数据类型(PLC的数据类型)。</param> | 
|         /// <param name="value">要写入的数据。</param> | 
|         /// <returns>如果写入成功则返回true,失败则抛出自定义通讯异常。</returns> | 
|         /// <exception cref="CommunicationException"></exception> | 
|         public override bool WriteObj(string address, string dataType, [NotNull] object value) | 
|         { | 
|             bool obj = false; | 
|             switch (dataType.ToLower()) | 
|             { | 
|                 case SiemensDBDataType.DataType_DInt: | 
|                     { | 
|                         int writeVal; | 
|                         try | 
|                         { | 
|                             writeVal = Convert.ToInt32(value); | 
|                         } | 
|                         catch (Exception ex) | 
|                         { | 
|                             throw new CommunicationException(string.Format(CommunicationExceptionMessage.TypeConvertError, dataType, address, value, ex.Message), CommunicationErrorType.TypeError, innerException: ex); | 
|                         } | 
|                         obj = GetResult(Write(address, writeVal), address, writeVal); | 
|                     } | 
|                     break; | 
|                 case SiemensDBDataType.DataType_DW: | 
|                     { | 
|                         uint writeVal; | 
|                         try | 
|                         { | 
|                             writeVal = Convert.ToUInt32(value); | 
|                         } | 
|                         catch (Exception ex) | 
|                         { | 
|                             throw new CommunicationException(string.Format(CommunicationExceptionMessage.TypeConvertError, dataType, address, value, ex.Message), CommunicationErrorType.TypeError, innerException: ex); | 
|                         } | 
|                         obj = GetResult(Write(address, writeVal), address, writeVal); | 
|                     } | 
|                     break; | 
|                 case SiemensDBDataType.DataType_Int: | 
|                     { | 
|                         short writeVal; | 
|                         try | 
|                         { | 
|                             writeVal = Convert.ToInt16(value); | 
|                         } | 
|                         catch (Exception ex) | 
|                         { | 
|                             throw new CommunicationException(string.Format(CommunicationExceptionMessage.TypeConvertError, dataType, address, value, ex.Message), CommunicationErrorType.TypeError, innerException: ex); | 
|                         } | 
|                         obj = GetResult(Write(address, writeVal), address, writeVal); | 
|                     } | 
|                     break; | 
|                 case SiemensDBDataType.DataType_W: | 
|                     { | 
|                         ushort writeVal; | 
|                         try | 
|                         { | 
|                             writeVal = Convert.ToUInt16(value); | 
|                         } | 
|                         catch (Exception ex) | 
|                         { | 
|                             throw new CommunicationException(string.Format(CommunicationExceptionMessage.TypeConvertError, dataType, address, value, ex.Message), CommunicationErrorType.TypeError, innerException: ex); | 
|                         } | 
|                         obj = GetResult(Write(address, writeVal), address, writeVal); | 
|                     } | 
|                     break; | 
|                 case SiemensDBDataType.DataType_Float: | 
|                     { | 
|                         float writeVal; | 
|                         try | 
|                         { | 
|                             writeVal = Convert.ToSingle(value); | 
|                         } | 
|                         catch (Exception ex) | 
|                         { | 
|                             throw new CommunicationException(string.Format(CommunicationExceptionMessage.TypeConvertError, dataType, address, value, ex.Message), CommunicationErrorType.TypeError, innerException: ex); | 
|                         } | 
|                         obj = GetResult(Write(address, writeVal), address, writeVal); | 
|                     } | 
|                     break; | 
|                 case SiemensDBDataType.DataType_Bool: | 
|                     { | 
|                         bool writeVal; | 
|                         try | 
|                         { | 
|                             writeVal = Convert.ToBoolean(value); | 
|                         } | 
|                         catch (Exception ex) | 
|                         { | 
|                             throw new CommunicationException(string.Format(CommunicationExceptionMessage.TypeConvertError, dataType, address, value, ex.Message), CommunicationErrorType.TypeError, innerException: ex); | 
|                         } | 
|                         obj = GetResult(Write(address, writeVal), address, writeVal); | 
|                     } | 
|                     break; | 
|                 case SiemensDBDataType.DataType_Byte: | 
|                     { | 
|                         byte writeVal; | 
|                         try | 
|                         { | 
|                             writeVal = Convert.ToByte(value); | 
|                         } | 
|                         catch (Exception ex) | 
|                         { | 
|                             throw new CommunicationException(string.Format(CommunicationExceptionMessage.TypeConvertError, dataType, address, value, ex.Message), CommunicationErrorType.TypeError, innerException: ex); | 
|                         } | 
|                         obj = GetResult(Write(address, writeVal), address, writeVal); | 
|                     } | 
|                     break; | 
|                 case SiemensDBDataType.DataType_String: | 
|                     { | 
|                         string writeVal; | 
|                         try | 
|                         { | 
|                             writeVal = value.ToString(); | 
|                         } | 
|                         catch (Exception ex) | 
|                         { | 
|                             throw new CommunicationException(string.Format(CommunicationExceptionMessage.TypeConvertError, dataType, address, value, ex.Message), CommunicationErrorType.TypeError, innerException: ex); | 
|                         } | 
|                         obj = GetResult(Write(address, writeVal), address, writeVal); | 
|                     } | 
|   | 
|                     break; | 
|                 case SiemensDBDataType.DataType_Char: | 
|   | 
|                     break; | 
|                 default: | 
|                     throw new CommunicationException(string.Format(CommunicationExceptionMessage.DataTypeErrorException, dataType, address), CommunicationErrorType.TypeError); | 
|             } | 
|             return obj; | 
|         } | 
|         #endregion | 
|   | 
|         #region ReadCustomer | 
|         public override T ReadCustomer<T>(string address) | 
|         { | 
|             try | 
|             { | 
|                 return plc.ReadCustomer<T>(address).Content; | 
|             } | 
|             catch (Exception ex) | 
|             { | 
|                 LogNet.WriteException(Name, $"【{Name}】PLC读取异常,地址:【{address}】,错误信息:【{ex.Message}】", ex); | 
|                 throw new CommunicationException(ex.Message, CommunicationErrorType.ReadException, innerException: ex); | 
|             } | 
|   | 
|         } | 
|         #endregion | 
|   | 
|         #region WriteCustomer | 
|         public override bool WriteCustomer<T>(string address, [NotNull] T value) | 
|         { | 
|             StringBuilder stringBuilder = new StringBuilder(); | 
|             try | 
|             { | 
|                 OperateResult operateResult = plc.WriteCustomer(address, value); | 
|                 stringBuilder.AppendLine(string.Format(CommunicationInfoMessage.WriteData, address, JsonConvert.SerializeObject(value))); | 
|                 if (operateResult.IsSuccess) | 
|                 { | 
|                     object? obj = null; | 
|                     for (int i = 0; i < 5; i++) | 
|                     { | 
|                         T readValue = ReadCustomer<T>(address); | 
|                         stringBuilder.AppendLine(string.Format(CommunicationInfoMessage.WriteAfterRead, address, JsonConvert.SerializeObject(readValue))); | 
|                         obj = readValue; | 
|                         PropertyInfo[] propertyInfos = typeof(T).GetProperties(); | 
|                         for (int j = 0; j < propertyInfos.Length; j++) | 
|                         { | 
|                             object? writeValueItem = propertyInfos[j].GetValue(value); | 
|                             object? readValueItem = propertyInfos[j].GetValue(readValue); | 
|                             if (writeValueItem.Equals(readValueItem)) | 
|                             { | 
|                                 stringBuilder.AppendLine(string.Format(CommunicationInfoMessage.WriteAndReadCheckSuccess, address, JsonConvert.SerializeObject(value), JsonConvert.SerializeObject(readValue))); | 
|                             } | 
|                             else | 
|                             { | 
|                                 break; | 
|                             } | 
|                             if (j == propertyInfos.Length - 1) | 
|                                 return true; | 
|                         } | 
|   | 
|                         plc.WriteCustomer(address, value); | 
|                     } | 
|                     stringBuilder.AppendLine(string.Format(CommunicationExceptionMessage.WriteAndReadCheckFaild, address, JsonConvert.SerializeObject(value), JsonConvert.SerializeObject(obj))); | 
|                     throw new CommunicationException(string.Format(CommunicationExceptionMessage.WriteAndReadCheckFaild, address, JsonConvert.SerializeObject(value), JsonConvert.SerializeObject(obj)), CommunicationErrorType.WriteFailed); | 
|                 } | 
|                 else | 
|                 { | 
|                     throw new CommunicationException(string.Format(CommunicationExceptionMessage.WriteFailedException, typeof(T).Name, address, JsonConvert.SerializeObject(value), operateResult.Message), CommunicationErrorType.WriteFailed); | 
|                 } | 
|             } | 
|             catch (Exception ex) | 
|             { | 
|                 LogNet.WriteException(Name, ex.Message, ex); | 
|                 throw new CommunicationException(ex.Message, CommunicationErrorType.WriteFailed, innerException: ex); | 
|             } | 
|             finally | 
|             { | 
|                 LogNet.WriteInfo(Name, stringBuilder.ToString()); | 
|             } | 
|   | 
|         } | 
|         #endregion | 
|   | 
|         // 显式实现IDisposable接口以提供垃圾回收时的清理   | 
|         public override void Dispose() | 
|         { | 
|             _isPing = false; | 
|             Disconnect(); | 
|             plc.Dispose(); | 
|             GC.SuppressFinalize(this); | 
|         } | 
|   | 
|         public override OperateResult<TimeSpan> Wait<T>(string address, int readInterval, int waitTimeout, T value) | 
|         { | 
|             TypeCode typeCode = Type.GetTypeCode(typeof(T)); | 
|             switch (typeCode) | 
|             { | 
|                 case TypeCode.Byte: | 
|                     DateTime start = DateTime.Now; | 
|                     while (true) | 
|                     { | 
|                         OperateResult<byte> read = plc.ReadByte(address); | 
|                         if (!read.IsSuccess) return OperateResult.CreateFailedResult<TimeSpan>(read); | 
|   | 
|                         if (read.Content == Convert.ToByte(value)) return OperateResult.CreateSuccessResult(DateTime.Now - start); | 
|                         if (waitTimeout > 0 && (DateTime.Now - start).TotalMilliseconds > waitTimeout) | 
|                         { | 
|                             return new OperateResult<TimeSpan>(StringResources.Language.CheckDataTimeout + waitTimeout); | 
|                         } | 
|                         HslHelper.ThreadSleep(readInterval); | 
|                     } | 
|                 case TypeCode.Int16: | 
|                     OperateResult<TimeSpan> operateResultShort = plc.Wait(address, Convert.ToInt16(value), readInterval, waitTimeout); | 
|                     return operateResultShort; | 
|                 case TypeCode.Int32: | 
|                     OperateResult<TimeSpan> operateResultInt = plc.Wait(address, Convert.ToInt16(value), readInterval, waitTimeout); | 
|                     return operateResultInt; | 
|                 case TypeCode.UInt16: | 
|                     OperateResult<TimeSpan> operateResultUShort = plc.Wait(address, Convert.ToInt16(value), readInterval, waitTimeout); | 
|                     return operateResultUShort; | 
|                 case TypeCode.UInt32: | 
|                     OperateResult<TimeSpan> operateResultUInt = plc.Wait(address, Convert.ToInt16(value), readInterval, waitTimeout); | 
|                     return operateResultUInt; | 
|                 default: | 
|                     throw new NotSupportedException(); | 
|             } | 
|         } | 
|         #endregion | 
|   | 
|         #region Destruction Function | 
|         /// <summary> | 
|         /// 析构函数,确保在不再需要时关闭连接   | 
|         /// </summary> | 
|         ~SiemensS7() | 
|         { | 
|             Dispose(); | 
|         } | 
|         #endregion | 
|     } | 
| } |