日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > C# >内容正文

C#

开源纯C#工控网关+组态软件(八)表达式编译器

發(fā)布時(shí)間:2023/12/4 C# 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 开源纯C#工控网关+组态软件(八)表达式编译器 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、???引子

?監(jiān)控畫面的主要功能之一就是跟蹤下位機(jī)變量變化,并將這些變化展現(xiàn)為動(dòng)畫。大部分時(shí)候,界面上一個(gè)圖元組件的某個(gè)狀態(tài),與單一變量Tag綁定,比如電機(jī)的運(yùn)行態(tài),綁定一個(gè)MotorRunning信號(hào);但有些時(shí)候不會(huì)這么簡(jiǎn)單,比如溫度計(jì)在溫度高于50℃顯示紅色;某設(shè)備報(bào)警,可能是多個(gè)條件其中之一觸發(fā)的結(jié)果;變量變化觸發(fā)一系列連鎖反應(yīng)…如此種種??紤]到工控行業(yè)大部分技術(shù)人員并非計(jì)算機(jī)專業(yè)出身,如何能夠用最少的編碼解決各種復(fù)雜的變量-動(dòng)畫綁定問(wèn)題,無(wú)疑要費(fèi)一番心思。

二、???方案選型

針對(duì)變量動(dòng)畫綁定問(wèn)題,可以選擇的方案包括如下幾種:

  • 腳本編譯器

不少大型組態(tài)軟件包含強(qiáng)大的腳本編輯器,支持諸如VBS、Python甚至C腳本語(yǔ)言。腳本自帶語(yǔ)法編輯器、調(diào)試器和編譯器,調(diào)用的API包羅萬(wàn)象,如數(shù)據(jù)庫(kù)API,通訊API,畫面組態(tài)API…可以用腳本實(shí)現(xiàn)非常復(fù)雜的邏輯。

但基于下面幾種考慮,我沒(méi)有實(shí)現(xiàn)這類的腳本編譯器:

  • 不同于大部分組態(tài)軟件包含一個(gè)獨(dú)立的界面設(shè)計(jì)器,我用Visual Studio來(lái)肩挑語(yǔ)法編輯、調(diào)試、編譯和界面設(shè)計(jì)的重任,沒(méi)必要多此一舉的搞一個(gè)獨(dú)立的腳本編譯器。

  • C#結(jié)合Visual Studio來(lái)調(diào)用通訊、數(shù)據(jù)庫(kù)鏈接的各類函數(shù),C#包含強(qiáng)大的語(yǔ)法功能,配合.NET 類庫(kù)幾乎無(wú)所不能,同時(shí)C#也支持腳本化,沒(méi)有必要在使用其他腳本語(yǔ)言。

  • 對(duì)于復(fù)雜的邏輯,就讓C#配合VS神器來(lái)完成吧。

    • 運(yùn)算符重載。

    曾經(jīng)研究過(guò)一個(gè)C#寫的腳本編譯系統(tǒng),它可以實(shí)現(xiàn)兩個(gè)特定集合間的四則運(yùn)算和邏輯運(yùn)算,如List1.A+List2.A;List1.A>List2.B??瓷先ゼ暇拖褚粋€(gè)普通的數(shù)值那樣參與運(yùn)算和操作。

    運(yùn)算符重載是C#一個(gè)強(qiáng)大的語(yǔ)法功能,可以重載的操作符如下:

    運(yùn)算符

    可重載性

    +、-、!、~、++、--、true、false

    可以重載這些一元運(yùn)算符。
    ? true和false運(yùn)算符必須成對(duì)重載。

    +、-、*、/、%、&、|、^、<<、>>

    可以重載這些二元運(yùn)算符。

    ==、!=、<、>、<=、>=

    可以重載比較運(yùn)算符。必須成對(duì)重載。

    &&、||

    不能重載條件邏輯運(yùn)算符。
    ? 但可以使用能夠重載的&和|進(jìn)行計(jì)算。

    []

    不能重載數(shù)組索引運(yùn)算符,但可以定義索引器。

    ()

    不能重載轉(zhuǎn)換運(yùn)算符,但可以定義新的轉(zhuǎn)換運(yùn)算符。

    +=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=

    不能顯式重載賦值運(yùn)算符。
    ? 在重寫單個(gè)運(yùn)算符如+、-、%時(shí),它們會(huì)被隱式重寫。

    =、.、?:、->、new、is、sizeof、typeof


    無(wú)疑運(yùn)算符重載用的好可以寫出語(yǔ)義更清晰、更簡(jiǎn)潔的代碼。

    比如有一種復(fù)數(shù)類型Complex,有兩個(gè)坐標(biāo)x和y;定義ComplexA大于ComplexB為: A的x,y中至少有一個(gè)大于B的x,y。我只需要重載>操作符(相應(yīng)的最好重載>=,<,<=),以后只需要A>B就能代替重復(fù)啰嗦的A.x>B.x||A.y>B.y。更可喜的是,重載后的>,<這些運(yùn)算符,在.Net表達(dá)式樹(shù)(ExpressionTree)中已經(jīng)替換了它原來(lái)的語(yǔ)義。因此運(yùn)算符重載在我這個(gè)編譯器也有它用武之地。

    但出于下面兩個(gè)原因,它只適合作為編譯引擎的輔助,而不適合單獨(dú)使用:

  • 首先運(yùn)算符重載只針對(duì)特定的類型;對(duì)于不熟悉C#語(yǔ)法特性的編程者,理解并正確的使用運(yùn)算符重載不是件容易的事。

  • 運(yùn)算符重載可以減少重復(fù)的代碼,讓語(yǔ)法更簡(jiǎn)潔;但依然要寫C#代碼,不適合大部分工控人員。

    • ?訂閱事件

    如果想省事,最簡(jiǎn)單的辦法是直接寫代碼,例如:如果一臺(tái)電機(jī)的運(yùn)行需要A,B,C三個(gè)前提條件均滿足,我就分別訂閱A、B、C的變量變化事件,如果A由fasle變?yōu)閠rue,再看看其他兩個(gè)變量觸發(fā)沒(méi)有。也就是寫這樣幾行代碼:

    ? ? ? ? ? ?var tag1 = App.Server["A"];

    ? ? ? ? ? ? var tag2 = App.Server["B"];

    ? ? ? ? ? ? var tag3 = App.Server["C"];

    ? ? ? ? ? ? if (tag1 != null && tag2 != null && tag3 != null

    ? ? ? ? ? ? {

    ? ? ? ? ? ? ? ? tag1.ValueChanged += (s, e) =>

    ? ? ? ? ? ? ? ? ? {

    ? ? ? ? ? ? ? ? ? ? ? if (tag1.Value.Boolean && tag2.Value.Boolean && tag3.Value.Boolean)

    ? ? ? ? ? ? ? ? ? ? ? {

    ? ? ? ? ? ? ? ? ? ? ? ? ? //執(zhí)行

    ? ? ? ? ? ? ? ? ? ? ? }

    ? ? ? ? ? ? ? ? ? };

    ? ? ? ? ? ? ? ? tag2.ValueChanged += (s, e) =>

    ? ? ? ? ? ? ? ? {

    ? ? ? ? ? ? ? ? ? ? if (tag1.Value.Boolean && tag2.Value.Boolean && tag3.Value.Boolean)

    ? ? ? ? ? ? ? ? ? ? {

    ? ? ? ? ? ? ? ? ? ? ? ? //執(zhí)行

    ? ? ? ? ? ? ? ? ? ? }

    ? ? ? ? ? ? ? ? };

    ? ? ? ? ? ? ? ? tag3.ValueChanged += (s, e) =>

    ? ? ? ? ? ? ? ? {

    ? ? ? ? ? ? ? ? ? ? if (tag1.Value.Boolean && tag2.Value.Boolean && tag3.Value.Boolean)

    ? ? ? ? ? ? ? ? ? ? {

    ? ? ? ? ? ? ? ? ? ? ? ? //執(zhí)行

    ? ? ? ? ? ? ? ? ? ? }

    ? ? ? ? ? ? ? ? };

    ? ? ? ? ? ? }

    看上去不算復(fù)雜吧?如果界面上有50個(gè)動(dòng)畫,這樣的代碼就要寫50次。不但浪費(fèi)時(shí)間,改起來(lái)麻煩,查起來(lái)也麻煩。更糟糕的是,不懂編程的人還用不了。

    • ?表達(dá)式編譯器

    對(duì)于大部分零編程基礎(chǔ)的上位機(jī)設(shè)計(jì)人員,他們需要的是一種沒(méi)有學(xué)習(xí)和理解成本的、簡(jiǎn)單直觀的變量綁定方式。

    比如溫度計(jì)在溫度高于50℃顯示紅色,就一句話【temperature>50】;某設(shè)備顯示報(bào)警,可能是多個(gè)報(bào)警變量其中之一觸發(fā)的結(jié)果,只需寫【Alarm1||Alarm2||Alarm3】…借助微軟強(qiáng)大的表達(dá)式引擎,如果能解析這類變量表達(dá)式,設(shè)計(jì)者只需要知道圖元與變量的邏輯關(guān)系;而極少數(shù)表達(dá)式也難以企及的功能,略微懂一點(diǎn)C#就可以實(shí)現(xiàn)。這樣就可以做到使用簡(jiǎn)單,上手容易,同時(shí)又可以滿足復(fù)雜的需求。

    同時(shí)還有下面幾個(gè)額外的好處:

    最少的編碼量:在一個(gè)界面的cs文件里,幾乎沒(méi)有代碼。綁定邏輯在XAML內(nèi)用直觀的方式嵌入:

    ?

  • 可以用復(fù)制、粘貼和文本替換等功能減少重復(fù)編碼;

  • 可以充分利用WPF的設(shè)計(jì)器擴(kuò)展,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的語(yǔ)法編輯器,實(shí)現(xiàn)語(yǔ)法高亮、自動(dòng)完成并執(zhí)行語(yǔ)法檢查;

  • 查找變量邏輯和修改很方便。

  • 這個(gè)編譯器的主要代碼在Eval類。

    三、???自己實(shí)現(xiàn)一個(gè)編譯器

    • 編譯原理

    大學(xué)計(jì)算機(jī)都有一門編譯原理課程。當(dāng)年我也捧著一本教材,被“波蘭表達(dá)式”、“逆波蘭表達(dá)式”繞的云里霧里,然而逆波蘭表達(dá)式是實(shí)現(xiàn)編譯器的關(guān)鍵。

    逆波蘭表達(dá)式的優(yōu)勢(shì)在于只用兩種簡(jiǎn)單操作,入棧和出棧就可以搞定任何普通表達(dá)式的運(yùn)算。其運(yùn)算方式如下:

    如果當(dāng)前字符為變量或者為數(shù)字,則壓棧,如果是運(yùn)算符,則將棧頂兩個(gè)元素彈出作相應(yīng)運(yùn)算,結(jié)果再入棧,最后當(dāng)表達(dá)式掃描完后,棧里的就是結(jié)果。

    如何實(shí)現(xiàn)自己的編譯器,微軟已經(jīng)給大家現(xiàn)成的輪子了。微軟的Expression類提供了一套拼接、編譯Lambda表達(dá)式的完整方法,可以用它輕松定義你自己的語(yǔ)法。相關(guān)知識(shí)可以參考博客園 裝配腦袋?的自己動(dòng)手開(kāi)發(fā)編譯器系列文章:http://www.cnblogs.com/Ninputer/archive/2011/06/18/2084383.html。下面就以這個(gè)SCADA項(xiàng)目為例:

    • 定義語(yǔ)法

    在這一版,我只實(shí)現(xiàn)了最基本最常用的一些操作,如四則運(yùn)算(+-*/)、邏輯運(yùn)算(&|!)、取反取模、三目條件等運(yùn)算。

    GetOperatorLevel函數(shù)按照C#的運(yùn)算符優(yōu)先級(jí)定義運(yùn)算優(yōu)先級(jí)。

    定義了@開(kāi)頭的自定義函數(shù)如@Date取當(dāng)前日期、@App取當(dāng)前路徑等。

    IsConstant方法定義系統(tǒng)常數(shù),其中True/False表示邏輯常量,字符串常量用’’。

    • 編譯過(guò)程

    編譯過(guò)程就是將一個(gè)字符串轉(zhuǎn)換為一個(gè)帶返回值的函數(shù);函數(shù)的參數(shù)就是表達(dá)式相關(guān)的Tag的值。依次為:

  • RpnExpression方法:將中綴表達(dá)式轉(zhuǎn)換為逆波蘭表達(dá)式。用關(guān)鍵字將表達(dá)式字符串分割為一個(gè)數(shù)組;按照優(yōu)先級(jí)出棧入棧;返回一個(gè)逆波蘭表達(dá)式順序的字符串列表。

  • ComplieRpnExp方法:根據(jù)逆波蘭表達(dá)式順序,依次彈出運(yùn)算符轉(zhuǎn)換為Expression的各子類如二元表達(dá)式BinaryExpression、條件表達(dá)式ConditionalExpression、常數(shù)表達(dá)式ConstantExpression等;參數(shù)首先判斷是否常數(shù),如果不是,則調(diào)用GetTagExpression方法,將字符串轉(zhuǎn)換為方法調(diào)用MethodCallExpression,最終會(huì)將該參數(shù)編譯為一個(gè)Tag。經(jīng)過(guò)處理最終返回一個(gè)LambdaExpression。

  • Eval方法將LambdaExpression編譯為一個(gè)委托;相關(guān)的Tag加入列表TagList。

  • 四、???應(yīng)用場(chǎng)景

    • ? 表達(dá)式與動(dòng)畫綁定

    在每一個(gè)界面窗體都有幾乎一樣的幾行代碼:

    List<TagNodeHandle> _valueChangedList;


    ? ? ? ? private void HMI_Loaded(object sender, RoutedEventArgs e)

    ? ? ? ? {

    ? ? ? ? ? ? lock (this)

    ? ? ? ? ? ? {

    ? ? ? ? ? ? ? ? _valueChangedList = cvs1.BindingToServer(App.Server);

    ? ? ? ? ? ? }

    ? ? ? ? }

    ?

    ? ? ? ? private void HMI_Unloaded(object sender, RoutedEventArgs e)

    ? ? ? ? {

    ? ? ? ? ? ? lock (this)

    ? ? ? ? ? ? {

    ? ? ? ? ? ? ? ?App.Server.RemoveHandles(_valueChangedList);

    ? ? ? ? ? ? }

    ? ? ? ? }

    其中,?BindingToServer就是對(duì)當(dāng)前界面所有圖元進(jìn)行地毯式掃描,搜索出各控件相關(guān)的TagReadText表達(dá)式并用Eval類編譯之;編譯的結(jié)果轉(zhuǎn)換為帶返回值的函數(shù)和一個(gè)相關(guān)Tag的列表;遍歷這個(gè)Tag列表,將其值變化事件ValueChanged與這個(gè)函數(shù)鏈接起來(lái)。這樣,在加載界面的時(shí)候已經(jīng)完成了編譯過(guò)程,相關(guān)變量的值一旦改變,就會(huì)根據(jù)表達(dá)式返回一個(gè)值,如果這個(gè)值是布爾量,同時(shí)與電機(jī)的運(yùn)行動(dòng)畫綁定,就完成了從表達(dá)式到動(dòng)畫的觸發(fā)過(guò)程。

    • ? 復(fù)雜報(bào)警條件

    報(bào)警一般包括超限報(bào)警、變量觸發(fā)報(bào)警、差值報(bào)警等。但也可能有復(fù)雜的報(bào)警條件,不能用超限、超差等簡(jiǎn)單方式表述的,就可以歸結(jié)為復(fù)雜報(bào)警,其條件可以用類似動(dòng)畫綁定的表達(dá)式來(lái)描述,在系統(tǒng)初始化時(shí)刻加載、編譯為報(bào)警條件。

    • ?未來(lái)改進(jìn)

    編輯器改進(jìn):支持命令自動(dòng)完成、語(yǔ)法高亮、更完善的語(yǔ)法檢查。可考慮Sharpdevelop的編輯控件。

    支持復(fù)雜語(yǔ)法:目前的語(yǔ)法僅僅是簡(jiǎn)單的四則運(yùn)算和邏輯表達(dá)式。未來(lái)考慮支持多段表達(dá)式、函數(shù)(如正余弦)、屬性引用等復(fù)雜語(yǔ)法。

    ?

    github地址:https://github.com/GavinYellow/SharpSCADA。QQ群:102486275

    相關(guān)文章:?

    • .NET十年回顧

    • 開(kāi)源純C#工控網(wǎng)關(guān)+組態(tài)軟件

    • 開(kāi)源純C#工控網(wǎng)關(guān)+組態(tài)軟件(三)加入一個(gè)新驅(qū)動(dòng):西門子S7

    • 開(kāi)源純C#工控網(wǎng)關(guān)+組態(tài)軟件(四)上下位機(jī)通訊原理

    • 開(kāi)源純C#工控網(wǎng)關(guān)+組態(tài)軟件(五)從網(wǎng)關(guān)到人機(jī)界面

    • 開(kāi)源純C#工控網(wǎng)關(guān)+組態(tài)軟件(六)圖元組件

    • 開(kāi)源純C#工控網(wǎng)關(guān)+組態(tài)軟件(七)數(shù)據(jù)采集與歸檔


      原文地址:http://www.cnblogs.com/evilcat/p/8379640.html


      .NET社區(qū)新聞,深度好文,歡迎訪問(wèn)公眾號(hào)文章匯總 http://www.csharpkit.com

      創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

      總結(jié)

      以上是生活随笔為你收集整理的开源纯C#工控网关+组态软件(八)表达式编译器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

      如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。