HslCommunication的OperateResult的使用细节说明,结果链操作示例。

本篇博文主要说说hslcommunication的结果链的知识,说一下前因后果,以及目前最新的功能扩充,(V9.5.0以上)

以前也写过一篇文章:https://www.cnblogs.com/dathlin/p/7865682.html 不看也没事,参考这篇新的文章就好了。

 

首先还是聊聊,为什么会诞生这个 OperateResult ,比如我有个方法,获取一些信息的,或是执行一些操作的,比如读取文件的内容。

		public string ReadFileContent( string path )
		{
			return System.IO.File.ReadAllText( path );
		}

  很简单吧,方法里面复杂也没有关系的,如果这个方法保证不会发生异常,或是失败,那就没有关系,这样写也挺好的,但是事实就是极容易发生异常,就拿这个例子来说,可能因为文件不存在,可能因为其他异常。如果我们需要返回的内容包含下面三大块,肯定包括 1. 是否成功   2.错误消息   3.内容       于是我加了一个错误码,就有了下面的类(以下是简写)

		public class OperateResult
                {
			public bool IsSuccess { get; set; }
			public string Message { get; set; }
			public int ErrorCode { get; set; }
		}

  然后可能携带各种不同类型的结果内容,又可能是多个的,所以有了泛型的派生类,这算是泛型的一个经典的例子,另一个例子就是List<T>数组了。

public class OperateResult<T> : OperateResult
public class OperateResult<T1, T2> : OperateResult
public class OperateResult<T1, T2, T3> : OperateResult
public class OperateResult<T1, T2, T3, T4> : OperateResult
public class OperateResult<T1, T2, T3, T4, T5> : OperateResult
public class OperateResult<T1, T2, T3, T4, T5, T6> : OperateResult
public class OperateResult<T1, T2, T3, T4, T5, T6, T7> : OperateResult
public class OperateResult<T1, T2, T3, T4, T5, T6, T7, T8> : OperateResult
public class OperateResult<T1, T2, T3, T4, T5, T6, T7, T8, T9> : OperateResult
public class OperateResult<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : OperateResult

  又定义了十个泛型类对象,最多可以携带10个不同类型的参数信息,当然,为了扩充一些转化信息,整个 OperateResult.cs 文件的源代码长达 3577 行源代码。

好了,所以上面的方法可以改写为:

		public OperateResult<string> ReadFileContent( string path )
		{
			try
			{
				return OperateResult.CreateSuccessResult( System.IO.File.ReadAllText( path ) );
			}
			catch(Exception ex)
			{
				return new OperateResult<string>( ex.Message );
			}
		}

  这样我们就能把结果信息返回了,当然了,实际可能更加复杂一点,比如下面所示,在读取文件之前,还需要检查当前账户是否有权限。

		public bool CheckPermission( )
		{
			// 检查账户合法性,是否有权利下载
			return true;
		}

		public OperateResult<string> ReadFileContent( string path )
		{
			if (!CheckPermission( )) return new OperateResult<string>( "当前无权读取文件的内容" );
			try
			{
				return OperateResult.CreateSuccessResult( System.IO.File.ReadAllText( path ) );
			}
			catch(Exception ex)
			{
				return new OperateResult<string>( ex.Message );
			}
		}

  到这里,已经成型基本的意思了。我们再来说一下HslCommunication自身的经典应用,我们来看一个三菱PLC的数据读取示例,我们为了要读取一个地址的原始字节数据,会提供这样的方法,

public override OperateResult<byte[]> Read( string address, ushort length )

  但是呢,实际上错误的原因是很多的,可能一开始地址输入错误了,可能网络发生了错误,可能PLC返回了一个错误码,然后进行解析得到正确的数据。那么底层这么实现

		public override OperateResult<byte[]> Read( string address, ushort length )
		{
			// 获取指令
			var command = BuildReadCommand(address, length, false, PLCNumber);
			if (!command.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(command);

			// 核心交互
			var read = ReadFromCoreServer(command.Content);
			if (!read.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(read);

			// 错误代码验证
			OperateResult check = CheckResponseLegal( read.Content );
			if (!check.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( check );

			// 数据解析,需要传入是否使用位的参数
			return ExtractActualData(read.Content, false);
		}

  我们再来看看这个核心交互是怎么实现的?

public OperateResult<byte[]> ReadFromCoreServer( byte[] send )
		{
			var result = new OperateResult<byte[]>( );
			OperateResult<Socket> resultSocket = null;

			InteractiveLock.Enter( );
			try
			{
				// 获取有用的网络通道,如果没有,就建立新的连接
				resultSocket = GetAvailableSocket( );
				if (!resultSocket.IsSuccess)
				{
					IsSocketError = true;
					AlienSession?.Offline( );
					InteractiveLock.Leave( );
					result.CopyErrorFromOther( resultSocket );
					return result;
				}

				OperateResult<byte[]> read = ReadFromCoreServer( resultSocket.Content, send );

				if (read.IsSuccess)
				{
					IsSocketError = false;
					result.IsSuccess = read.IsSuccess;
					result.Content = read.Content;
					result.Message = StringResources.Language.SuccessText;
				}
				else
				{
					IsSocketError = true;
					AlienSession?.Offline( );
					result.CopyErrorFromOther( read );
				}

				ExtraAfterReadFromCoreServer( read );
				InteractiveLock.Leave( );
			}
			catch
			{
				InteractiveLock.Leave( );
				throw;
			}

			if (!isPersistentConn) resultSocket?.Content?.Close( );
			return result;
		}

  我们可以看到,一旦中间的某个环节发生了错误或是异常,这个错误信息会一直向上传递,直到传递给最上层的调用者。以此形成上下的链条。

 

 

那么Convert,Check,Then是什么意思呢?主要是简化代码的。我们来看看下面的代码

		public OperateResult<string> Write( )
		{
			OperateResult write = siemens.Write( "M100", (short)12 );
			if (!write.IsSuccess) return OperateResult.CreateFailedResult<string>( write );

			return OperateResult.CreateSuccessResult( "M100写入成功" );
		}

  这个代码就可以简化为:

		public OperateResult<string> Write( ) => siemens.Write( "M100", (short)12 ).Convert<string>( "M100写入成功" );

  我们看到代码简化了很多,所以Convert意思就是,如果原来的结果对象失败,就直接返回,如果成功,就返回给定的结果内容。

 

 

我们再来看第二种情况:这种情况主要是对读取的内容进行一些判断操作。

		public OperateResult Check( )
		{
			OperateResult<short> read = siemens.ReadInt16( "M100" );
			if (!read.IsSuccess) return OperateResult.CreateFailedResult<string>( read );

			if (read.Content == 10) return OperateResult.CreateSuccessResult( );
			else return new OperateResult( "设备的数据值不对" );
		}

  这个代码可以简化为:

		public OperateResult Check( ) => siemens.ReadInt16( "M100" ).Check( m => m == 10, "设备的数据值不对" );

  当然,如果我的检查的方法比较复杂,也可以这么写:

		public OperateResult CheckStatus(short value )
		{
			if (value == 1) return new OperateResult( "错误原因1" );
			if (value == 2) return new OperateResult( "错误原因2" );
			if (value == 3) return new OperateResult( "错误原因3" );
			if (value == 4) return new OperateResult( "错误原因4" );
			return OperateResult.CreateSuccessResult( );
		}


		public OperateResult Check( ) => siemens.ReadInt16( "M100" ).Check( m => CheckStatus( m ) );

  

我们再来看看一种更复杂的情况。

		public OperateResult StartPLC( )
		{
			// 这是一个启动PLC的方法,逻辑就是,M100.0是启动PLC,但是在启动之前,需要向PLC的多个地址写入初始参数。
			OperateResult write = siemens.Write( "M200", (short)123 );
			if (!write.IsSuccess) return write;

			write = siemens.Write( "M202", 123f );
			if (!write.IsSuccess) return write;

			write = siemens.Write( "M206", "123456" );
			if (!write.IsSuccess) return write;

			return siemens.Write( "M100.0", true );
		}

  嗯,这时候,就需要使用Then方法了,可以简化为:

		public OperateResult StartPLC( ) => siemens.Write( "M200", (short)123 ).
			Then( ( ) => siemens.Write( "M202", 123f ) ).
			Then( ( ) => siemens.Write( "M206", "123456" ) ).
			Then( ( ) => siemens.Write( "M100.0", true ) );

  

 

 

一旦发生失败,就会立即回传。现在我们来看个更复杂的综合例子,这是一个现场流程中间的一个小环节,当AGV车到达库位后,需要通知PLC进行连串的交互,以及读取条码信息:

  

		string barcode = string.Empty;
		public OperateResult CheckSignalAfterAgvReach( )
		{
			// 通知PLC信息,AGV已经到达
			OperateResult write = siemens.Write( "DB101.3.1", true );
			if (!write.IsSuccess) return write;

			// 等待PLC复位 允许AGV放胚信号 为false
			OperateResult wait = siemens.Wait( "DB101.3.2", false );
			if (wait.IsSuccess) return wait;

			// 复位AGV放胚完成信号
			write = siemens.Write( "DB101.3.1", false );
			if (!write.IsSuccess) return write;

			// 等待允许读取条码信息
			wait = siemens.Wait( "DB101.1.3", true );
			if (wait.IsSuccess) return wait;

			// 读取条码的信息
			var readBarCode = siemens.ReadString( "DB102.0" );
			if (!readBarCode.IsSuccess) return readBarCode;

			// 条码用于其他用途
			barcode = readBarCode.Content;

			// 将上料读取条码完成值true
			write = siemens.Write( "DB101.1.4", true );
			if (!write.IsSuccess) return write;

			// 等待上料允许读取条码设置为false
			wait = siemens.Wait( "DB101.1.3", false );
			if (wait.IsSuccess) return wait;

			// 复位上料条码读取完成信号
			return siemens.Write( "DB101.1.4", false );
		}

  那么这部分的代码可以简写为:

		public OperateResult CheckSignalAfterAgvReach2( ) => siemens.Write( "DB101.3.1", true ).
			Then( ( ) => siemens.Wait( "DB101.3.2", false ) ).
			Then( ( ) => siemens.Write( "DB101.3.1", false ) ).
			Then( ( ) => siemens.Wait( "DB101.1.3", true ) ).
			Then( ( ) => siemens.ReadString( "DB102.0" ) ).
			Then( m => { barcode = m; return siemens.Write( "DB101.1.4", true ); } ).
			Then( ( ) => siemens.Wait( "DB101.1.3", false ) ).
			Then( ( ) => siemens.Write( "DB101.1.4", false ) );

		string barcode = string.Empty;

  emmmm,好像写多了,代码是简化了,可读性并没有提升很多,也是给了一个方向。

 

posted @ 2020-10-23 11:21  dathlin  阅读(6040)  评论(0编辑  收藏  举报