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,好像寫多了,代碼是簡化了,可讀性並沒有提升很多,也是給了一個方向。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM