今天介紹一下如何在C#側調用Python腳本,並且做一些有趣的實驗。
首先介紹一下今天的主角,IronPython,大名鼎鼎,想要了解的可以去它的官網看看相關的介紹,很帶勁。http://ironpython.net/
新建一個C#的控制台項目,然后使用nuget安裝這個IronPython組件
Install-Package IronPython
好了,開始學習吧。
首先我們需要理解,為什么會需求腳本?沒有腳本行不行?當然行啊!但是我們可以想象一下一個需求。我們從設備獲取到了一個數據,比如是 float a = 100f; 但是這個數不是最終的數,而是需要運算的,比如是要縮小10倍才是正確的數。
那么我們會 a = a / 10; 如果業務變成了會縮小N倍,這個倍數不一定,經常會調整。好了,我們就把這個倍數做成了配置項,保存在txt里,然后從txt加載倍數,然后來動態調整,這時候我們的程序已經很靈活了。
在txt里寫入 10 就是縮小10倍,寫入0.5就是放大兩倍。OK,現在業務變得更加的復雜了!我不一定是乘除法,也可能是加減法,甚至是組合運算加減乘除法。這時候你會怎么做呢?
傳統的方式,我還是做配置文件,把每種情況都羅列出來,1代表乘除法,2代表加減法,3代表什么什么,等等,每種情況再做配置文件,配置項不一樣嘛。這種方式當然也可以實現,只是
1. 比較麻煩,需要寫大量的配置代碼,讀寫文件的代碼。
2. 仍然適應不了未來的變化,以后可能業務又更改了,而你一開始沒有考慮到,又要改源代碼了,然后編譯,然后部署。
ok,現在可以嘗試一種全新的技術(其實腳本技術不算新),這里的新技術主要是對於剛接觸的人來說。
我們針對上面的需求進行實現。
我們現在程序的debug目錄下,新建一個hsl.py文件,方便我們的調用,然后我們使用VS CODE 進行編輯這個文件,關於如何安裝python及配置環境啥的,可以參考下面的文章:
https://www.cnblogs.com/dathlin/p/12142663.html
好了,現在開始編輯了,我們需要定義一個轉換的方法,如下所示
然后我們在C#里寫下面的代碼
static void Main( string[] args ) { float value = 123f; // 模擬我們獲取到的數據 Microsoft.Scripting.Hosting.ScriptEngine engine = IronPython.Hosting.Python.CreateEngine( ); dynamic script = engine.ExecuteFile( "hsl.py" ); float result = script.GetActulValue( value ); Console.WriteLine( $"Value Old:{value} New Value:{result}" ); Console.ReadLine( ); }
我們運行起來看看,看看會輸出什么?
這時候應該發出震撼的聲音,我去!居然真的可以,我們在看看修改下python腳本的代碼
看看結果
上述的例子太簡單了,我們來看看更高級的數學方法
我們改的更高級一點了。這個數可以算出啥。我也不知道了。所以我們看看,這玩意能輸出什么?
emmmmm.....報錯了,python的運算之后結果變成了double類型,應該是math處理方法的原因,所以我們的C#代碼要萬無一失的話,稍微改改
好了,看來我們可以用一些python自己的庫相關的代碼,都可以執行。接下來我們看看下面的py代碼
我們看看這個效果
這么看來也是沒有任何問題的。
這樣的話,就可以完成一些很高級的自定義的腳本操作了。
你以為到這里就結束了?接下來才是給力的部分。上述已經實現了文章開篇提出的需求了,接下來我們看看一個更高級的需求。
在C#里有五個方法。A,B,C,D,E代表了業務的五個部分,我們的主體業務是分別調用這五個方法,進行排列組合,甚至,有的不執行,或是執行多次。如果需要這種業務應該怎么辦呢?
同樣是腳本是最合適,我們需要在python里調用C#的這五個方法。
那么第一步就是定義這五個方法
public static void A( ) { Console.WriteLine( "Method A Called" ); } public static void B( ) { Console.WriteLine( "Method B Called" ); } public static void C( ) { Console.WriteLine( "Method C Called" ); } public static void D( ) { Console.WriteLine( "Method D Called" ); } public static void E( ) { Console.WriteLine( "Method E Called" ); }
很簡單,只要被調用一次,就會打印出記錄,方便我們跟蹤。
static void Main( string[] args ) { Microsoft.Scripting.Hosting.ScriptEngine engine = IronPython.Hosting.Python.CreateEngine( ); Microsoft.Scripting.Hosting.ScriptScope scope = engine.CreateScope( ); scope.SetVariable( "A", new Action( A ) ); scope.SetVariable( "B", new Action( B ) ); scope.SetVariable( "C", new Action( C ) ); scope.SetVariable( "D", new Action( D ) ); scope.SetVariable( "E", new Action( E ) ); engine.ExecuteFile( "hsl.py", scope ); Action business = scope.GetVariable<Action>( "MainBusiness" ); business( ); // 調用主業務現實 Console.ReadLine( ); }
這里我們不用C#的動態語法來執行腳本了,我們通過獲取委托的方式,當然了,我們先把這五個方法,傳進python里面去,就可以調用了,python的代碼如下,需要注意的是,方法名和上面的要一樣
ok,很簡單的,就是順序調用一下而已,好了,我們現在看看輸出
我去,真的可以啊,牛逼,不禁再次感嘆下,來來來,我們的腳本寫的更加復雜點。
我們還加入的循環體,來來來,繼續看看效果。
我去,牛逼!!!
再來看看變量呢?
我們新增加一個count變量,然后傳入到python腳本,看看python能不能獲取到
然后我們運行C#側的代碼
可以獲取到,我們現在來更改值看看
就是簡單的修改一個值。
發現沒有更新,那么可以推斷,傳入Python的值變量,只是數據的副本,那么我們應該傳入引用變量
我們定義了一個匿名類型,如果這部分不清楚,就可以去補補C#的知識了。
好了,我們再運行看看
額,,,,發生異常了。這里暫時還沒有想明白,不過暫時的解決可以通過返回值來解決,我們讓業務方法返回數據,進行更改。如果有網友知道怎么解決,非常感謝。
運行看效果。
OK,最后我們來看看,如果我還有一個py的腳本文件。實現另一個方法,F()
我需要在上面的腳本里調用這個方法。
我們同時加載第二個文件,然后更改第一個py文件的代碼
然后我們看運行效果。
ok,可以,非常好,剩下的細節就要結合實際開發了。接下來看一個例子:
我們在項目里面安裝 HslCommuncation
我們在C#的代碼里生成一個連接西門子的網絡對象類。並且把這個類傳遞給Python,那么代碼如下所示:
static void Main( string[] args ) { var data = new Good (){ Name = "BooK", Price = 10 }; Microsoft.Scripting.Hosting.ScriptEngine engine = IronPython.Hosting.Python.CreateEngine( ); Microsoft.Scripting.Hosting.ScriptScope scope = engine.CreateScope( ); scope.SetVariable( "Good", data ); scope.SetVariable( "A", new Action( A ) ); scope.SetVariable( "B", new Action( B ) ); scope.SetVariable( "C", new Action( C ) ); scope.SetVariable( "D", new Action( D ) ); scope.SetVariable( "E", new Action( E ) ); HslCommunication.Profinet.Siemens.SiemensS7Net siemens = new HslCommunication.Profinet.Siemens.SiemensS7Net( HslCommunication.Profinet.Siemens.SiemensPLCS.S1200, "192.168.8.12" ); siemens.SetPersistentConnection( ); scope.SetVariable( "siemens", siemens ); engine.ExecuteFile( "hsl.py", scope ); engine.ExecuteFile( "hsl2.py", scope ); Func<int> business = scope.GetVariable<Func<int>>( "MainBusiness" ); data.Price = Convert.ToInt32(business( )); // 調用主業務現實 Console.WriteLine( data.Price.ToString( ) ); siemens.ConnectClose( ); Console.ReadLine( ); }
然后在python里讀取西門子的數據信息。然后打印出來
ok,那么我們來執行
可以,非常給力。
我們再來看看寫入操作。
讀出來是0,應該是寫入的類型不對,那么我們需要寫入的是short類型,應該怎么操作呢?
這樣就可以讀取到我們需要的數據了。如果我們寫入的是數組呢?
我們自然而然想到:
結果報下面的錯誤。
意思就是兩個重載的方法不知道選哪個,好了,問題知道了,我們來修復下這個內容
到這里成功寫入,我們也拿到了自己的數據。
如果我需要使用 C#的類,這個類是我自己創建的話。
比如說這里的OperateResult
如果想使用線程的技術,可以使用C#的線程技術
from System.Threading import Thread, ThreadStart
def ThreadCheck(): count = 0 while True: count = count + 1 time.sleep(1) logNet.WriteDebug('線程檢測:'+ str(count)) if count > 10: break def SendMesCmdToPlc(cmd): Thread(ThreadStart(ThreadCheck)).Start()
就可以啟動線程的檢測
更詳細的英文版教程如下:
https://ironpython.net/documentation/dotnet/
關於變量賦值,如果有老鐵解決了,歡迎留言。