zoukankan      html  css  js  c++  java
  • 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,好像写多了,代码是简化了,可读性并没有提升很多,也是给了一个方向。

  • 相关阅读:
    Lucene in action 笔记 case study
    关于Restful Web Service的一些理解
    Lucene in action 笔记 analysis篇
    Lucene in action 笔记 index篇
    Lucene in action 笔记 term vector
    Lucene in action 笔记 search篇
    博客园开博记录
    数论(算法概述)
    DIV, IFRAME, Select, Span标签入门
    记一个较困难的SharePoint性能问题的分析和解决
  • 原文地址:https://www.cnblogs.com/dathlin/p/13863115.html
Copyright © 2011-2022 走看看