使用Lua腳本語言開發出高擴展性的系統,AgileEAS.NET SOA中間件Lua腳本引擎介紹

1、前言

     AgileEAS.NET SOA 中間件平臺是一款基於基於敏捷並行開發思想和Microsoft .Net構件(組件)開發技術而構建的一個快速開發應用平臺。用於幫助中小型軟件企業創建一條適合市場快速變化的開發團隊,以達到節省開發成本、縮短開發時間,快速適應市場變化的目的。git

     AgileEAS.NET SOA中間件平臺提供了敏捷快速開發軟件工程的最佳實踐,經過提供大量的基礎支撐功能如IOC、ORM、SOA、分佈式體系及敏捷併發開發方法所支撐的插件開發體系,以及提供了大量的實體、數據模型設計生成工具、代碼生成工具,用於幫助中小軟件開發商快速成長。程序員

     AgileEAS.NET平臺充分把握目前軟件行業快速發展的新趨勢,基於敏捷並行開發、快速適應市場這樣淳樸的軟件工程實踐,採用業界普遍使用的Microsoft .Net構件(組件)開發技術實踐了這種開發思想,幫助軟件企業實現「敏捷變化、快速適合」的目標,從而幫助軟件企業在激烈的市場競爭中贏得先機並得到更高的回報。github

2、關於Lua語言

     Lua 是一個小巧的腳本語言。是巴西里約熱內盧天主教大學(Pontifical Catholic University of Rio de Janeiro)裏的一個研究小組,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所組成並於1993年開發。 其設計目的是爲了嵌入應用程序中,從而爲應用程序提供靈活的擴展和定製功能。數據庫

     Lua語言目前應用最普遍的領域是遊戲編程領域,最先接觸和認識Lua也是由於在2008年玩金山的劍網3,經過解包讀過其中的一些遊戲腳本,慢慢也看過一些其餘遊戲的Lua腳本,在咱們開發本身的電子病歷系統的過程之中,引入了Lua腳本語言實現那些可變性很是高的場景。編程

    .NET經過LuaInterface開源項目類庫實現對Lua的腳本調用以及Lua與C#的相互綁定,有關於這此方面的內容請你們搜索相關文章以得到幫助。併發

     在AgileEAS.NET SOA5.0版本之中,咱們決定把在開發過程之中造成的Lua腳本引擎一併集成入AgileEAS.NET SOA中間件平臺,目前Lua腳本語言被咱們普遍的應用電子病歷系統這中的病案自動評分、短信系統之中的交互式應答、和一些計劃任系統之中的計劃任務定義。app

3、AgileEAS.NET SOA平臺Lua引擎

     AgileEAS.NET SOA5.0平臺目前使用的是Lua5.1版本,使用LuaInterface實現C#與Lua的相互綁定處理,平臺已經對其進行了二次封裝以提供統一的API支持,目前由EAS.LuaScript.dll程序集承載全部業務。框架

     AgileEAS.NET SOA平臺Lua引擎提供瞭如下接口或類型的API:分佈式

     ILuaEngine,定義爲一個Lua腳本引擎:函數

   1: using System;
   2:  
   3: namespace EAS.LuaScript
   4: {
   5:     /// <summary>  
   6:     /// Lua腳本解析引擎。  
   7:     /// </summary>  
   8:     /// <remarks>
   9:     /// 用於完成程序之中嵌入的動態Lua腳本,Lua腳本在AgileEAS.NET SOA 中間件之中主要用於環境的各類參數的動態解析處理之中。
  10:     /// </remarks>
  11:     public interface ILuaEngine:IDisposable
  12:     {
  13:         /// <summary>
  14:         /// 腳本路徑。
  15:         /// </summary>
  16:         string ScriptDirectory
  17:         {
  18:             get;
  19:             set;
  20:         }
  21:  
  22:         /// <summary>
  23:         /// 輸出重定向方法/用於調試。
  24:         /// </summary>
  25:         Action<object> OutAction
  26:         {
  27:             get;
  28:             set;
  29:         }
  30:  
  31:         /// <summary>  
  32:         /// 註冊lua函數,實現Lua綁定。
  33:         /// </summary>  
  34:         /// <param name="luaAPIClass">lua函數類</param>  
  35:         void BindLuaFunctions(object luaAPIClass);
  36:  
  37:         /// <summary>  
  38:         /// 執行lua腳本文件。 
  39:         /// </summary>  
  40:         /// <param name="luaFileName">腳本文件名。</param>  
  41:         /// <returns>lua腳本執行結果。</returns>
  42:         object[] DoFile(string luaFileName);
  43:  
  44:         /// <summary>  
  45:         /// 執行lua腳本文本。
  46:         /// </summary>  
  47:         /// <param name="chunk">lua指令。</param>  
  48:         /// <returns>lua腳本執行結果。</returns>
  49:         object[] DoString(string chunk);
  50:  
  51:         /// <summary>
  52:         /// 調用Lua函數。
  53:         /// </summary>
  54:         /// <param name="luaFunction">函數名稱。</param>
  55:         /// <param name="args">調用參數。</param>
  56:         /// <returns>lua腳本執行結果。</returns>
  57:         object[] Invoke(string luaFunction, params object[] args);
  58:     }
  59: }

     其中腳本路徑ScriptDirectory指示Lua腳本文件的存儲位置,在DoFile執行腳本文過程之中若是傳入爲相關路徑則能夠自動在ScriptDirectory之中尋址,ScriptDirectory默認爲當前程序目錄。

     DoFile:執行腳本文件而且返回執行結果。

     DoString:執行腳本代碼而且返回執行結果。

     Invoke:執行指定的腳本函數。

      LuaFunctionAttribute,Lua腳本函數特性:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5:  
   6: namespace EAS.LuaScript
   7: {
   8:     /// <summary>  
   9:     /// Lua函數描述特性。
  10:     /// </summary>  
  11:     /// <remarks>
  12:     /// 用於標記Lua接口方法。
  13:     /// </remarks>
  14:     [AttributeUsage(AttributeTargets.Method,
  15:                     Inherited = false, AllowMultiple = false)]
  16:     [Serializable]
  17:     public class LuaFunctionAttribute : Attribute
  18:     {
  19:         /// <summary>
  20:         /// 初始化LuaFunctionAttribute對象實例。
  21:         /// </summary>
  22:         /// <param name="name">函數名稱。</param>
  23:         public LuaFunctionAttribute(string name)
  24:             : this(name, string.Empty)
  25:         {
  26:  
  27:         }
  28:  
  29:         /// <summary>
  30:         /// 初始化LuaFunctionAttribute對象實例。
  31:         /// </summary>
  32:         /// <param name="name">函數名稱。</param>
  33:         /// <param name="description">函數描述。</param>
  34:         public LuaFunctionAttribute(string name, string description)
  35:             : this(name, description,null)
  36:         {
  37:  
  38:         }
  39:  
  40:         /// <summary>
  41:         /// 初始化LuaFunctionAttribute對象實例。
  42:         /// </summary>
  43:         /// <param name="name">函數名稱。</param>
  44:         /// <param name="description">函數描述。</param>
  45:         /// <param name="funcParams">參數說明。</param>
  46:         public LuaFunctionAttribute(string name, string description, params string[] funcParams)
  47:         {
  48:             this.Name = name;
  49:             this.Description = description;
  50:             this.Params = funcParams;
  51:         }
  52:  
  53:         /// <summary>
  54:         /// 函數名稱。
  55:         /// </summary>
  56:         public string Name
  57:         {
  58:             get;
  59:             internal set;
  60:         }
  61:  
  62:         /// <summary>
  63:         /// 函數描述。
  64:         /// </summary>
  65:         public string Description
  66:         {
  67:             get;
  68:             internal set;
  69:         }
  70:  
  71:         /// <summary>
  72:         /// 參數說明。
  73:         /// </summary>
  74:         public string[] Params
  75:         {
  76:             get;
  77:             internal set;
  78:         }
  79:     }
  80: }

     用於C#向Lua暴露函數接口,實現C#方法與Lua腳本函數的綁定,也能夠實現爲把C#的方法暴露給Lua腳本調用,如如下代碼:

   1: /// <summary>
   2: /// 執行指定的非查詢命令。
   3: /// </summary>
   4: /// <param name="commandText">要執行的命令語句。該語句必須是標準的數據庫語句。</param>
   5: /// <returns>返回命令影響的行數。</returns>
   6: [LuaFunction("ExecuteSql", "執行指定的非查詢命令,返回執行所影響的行數。", "要執行的SQL語句")]
   7: public int Execute(string commandText)
   8: {
   9:     IDataAccessor da = this.GetComponent();
  10:     if (da != null)
  11:     {
  12:         return da.Execute(commandText);
  13:     }
  14:     else
  15:     {
  16:         return -1;
  17:     }
  18: }

     其目標早實現C#方法Execute與Lua腳本函數ExecuteSql之間的綁定,當在Lua教本之中調用ExecuteSql,即會執行Execute方法。

     LuaFramework,Lua框架,爲Lua腳本引用最重要的API:

   1: #region 程序集 EAS.LuaScript.dll, v4.0.30319
   2: // G:\Health.Work\AgileEMR4.0\Publish\EAS.LuaScript.dll
   3: #endregion
   4:  
   5: using System;
   6:  
   7: namespace EAS.LuaScript
   8: {
   9:     // 摘要:
  10:     //     Lua腳本執行框架/上下文。
  11:     //
  12:     // 備註:
  13:     //     應用系統之中調用Lua腳本的入口。
  14:     public sealed class LuaFramework
  15:     {
  16:         // 摘要:
  17:         //     LuaFramework對象的惟一實例。
  18:         public static LuaFramework Instance { get; }
  19:  
  20:         // 摘要:
  21:         //     註冊lua函數,實現Lua綁定。
  22:         //
  23:         // 參數:
  24:         //   luaAPIClass:
  25:         //     lua函數類
  26:         public static void BindLuaFunctions(object luaAPIClass);
  27:         //
  28:         // 摘要:
  29:         //     從應用程序上下文環境之中獲取指定名稱的對象實例。
  30:         //
  31:         // 參數:
  32:         //   componentKey:
  33:         //     組件名稱。
  34:         //
  35:         // 返回結果:
  36:         //     對象實例。
  37:         public object GetComponent(string componentKey);
  38:         //
  39:         // 摘要:
  40:         //     Lua腳本引擎。
  41:         public static ILuaEngine GetLuaEngine();
  42:         //
  43:         // 摘要:
  44:         //     Lua腳本引擎。
  45:         public static ILuaEngine GetLuaEngine(string scriptDirectory);
  46:         //
  47:         // 摘要:
  48:         //     取系統時間。
  49:         [LuaFunction("GetTime", "取系統時間")]
  50:         public DateTime GetTime();
  51:     }
  52: }

     其中BindLuaFunctions方法實現C#方法與Lua函數的綁定註冊,調用時傳入須要註冊方法所在的C#類對象實例便可。
     GetLuaEngine獲取一個ILuaEngine,爲以後的執行腳本做準備,調用GetLuaEngine可選傳入scriptDirectory參數,scriptDirectory爲Lua腳本存儲目錄。

     AgileEAS.NET SOA中間件已經爲你們提供了少數的幾個預約義API綁定:

   1: helpcmd(cmdName) - Show help for a given command or package
   2: help() - List available commands.
   3: Include(luaFileName) - 預加載指定的腳本文件
   4: echo(message) - 顯示Lua調試信息,用於Lua的動態調試。
   5: Echo(message) - 顯示Lua調試信息,用於Lua的動態調試。
   6: WriteLog(message) - 寫日誌記錄,用於Lua腳本的動態調試。
   7: GetTime() - 取系統時間
   8: GetComponent(componentKey) - 從應用程序上下文環境之中獲取指定名稱的對象實例
   9: GetAccount() - 求當前帳戶信息
  10: ExecuteSql(commandText) - 執行指定的非查詢命令,返回執行所影響的行數。
  11: QuerySql(commandText, resultType) - 執行給定的數據庫查詢命令,返回執行結果,返回結果由resultType參數決定。
  12: QuerySqlScalar(commandText) - 執行給定的數據庫查詢命令,僅返回第一行第一列結果
  13: QuerySqlMatrix(commandText) - 執行給定的數據庫查詢命令,返回第一個查詢結果Matrix
  14: QuerySqlDictionary(commandText) - 執行給定的數據庫查詢命令,返回第一行的Key-Value
  15: QuerySqlList(commandText) - 執行給定的數據庫查詢命令,返回第一行的Key-Value
  16: QuerySqlDataSet(commandText) - 執行給定的數據庫查詢命令,返回查詢結果集
  17: QuerySqlDataTable(commandText) - 執行給定的數據庫查詢命令,返回查詢結果集的第一個DataTable
  18: GetDataSet() - 求當前系統的賬套信息

     你們也能夠基於AgileEAS.NET SOA 平臺Lua引擎擴展本身的LuaAPI。

4、AgileEAS.NET SOA平臺Lua引擎應用案例

     在我12年的醫療行業開發之中,作過多年的電子病歷,開發、指導開發過多套電子病歷系統,在電子病歷系統之中,有如下幾個問題一直得不到好的解決方案:

     在電子病歷質控之中有一個自動質控評分,其由程序自動的預先爲某個病案打出一個得分:

]3L0{AR35LX4WKWXUAU}(2S

     在沒有使用Lua教本以前呢,也是能實現這種自動評分,可是爲了實現這樣的功能,系統設計之中就會要增長一些輔助的表和大量的輔助設計以幫助完成這樣的功能,由於畢竟不存在統一的評分規則,因此這個設計會極其的複雜,以至於很難把這樣的功能作好,或者說在成本和效益估量之中,得不償失。

     另外還有比較簡單的例子就是電子病歷之間的宏替換問題,在書寫電子病歷的過程之中須要動態的插入如病人基本信息、醫院名稱這樣的東西進入病歷,可是由於其數據源並非惟一肯定的,若是不使用動態腳本設計,那麼其程序這中就會存在大量的硬編碼,而且若是想要在後期實施階段增長宏,那麼則必需要修改程序才能完成。

     下面咱們就以自動評分案例向你們講解一下是如何實現功能的呢:

     首先,系統之中必須管理和維護用於自動評分的全部評分規則:

BF$CE1XPYCZES13DJ%Z4XP5

     這裏與其餘不少開發者的作法不同的是,對於每一個缺陷項目,咱們都爲其提供了一個評分規則的腳本的設計,例如對於缺陷「*缺入院記錄」的評分腳本以下:

   1: -----------------------------------------------------------------------------------------------
   2: -- 建立人   :  魏瓊東
   3: -- 建立時間    : 2013-09-05
   4: -- 效果備註    : *缺入院記錄評分腳本。
   5: --               GetErrorNumber,求缺陷數量,由腳本計算出本缺陷數量
   6: ----------------------------------------------------------------------------------------------
   7: ------------------------不怎麼華麗的分割線--------------------------------------
   8: function GetErrorNumber(patient,errorItem)
   9:  
  10:     --調用C#,求監控項目
  11:     local monitorItem = GetMonitorItem("B01")    
  12:     --監控表達式,無表達式返回0
  13:     local script = monitorItem.Expression
  14:     if script == nil then
  15:         return 0
  16:     elseif script == "" then
  17:         return 0
  18:     end    
  19:     
  20:     --加載監控腳本
  21:     ExecuteScript(script)
  22:     --執行監控函數NeedWrite,若是須要寫則預標存在此缺陷
  23:     local need = NeedWrite(patient,monitorItem)
  24:     
  25:     if need == 1 then
  26:         return 1
  27:     else
  28:         return 0
  29:     end
  30:  
  31: end

這樣的設計的目標是將缺陷的評分動做都由Lua腳本實現,而在應用程序之中並不存在針對缺陷制定不一樣的評分規則的狀況,只有調用腳本進行評分的代碼:

   1: /// <summary>
   2: /// 求指定患者、指定缺陷的缺陷數。
   3: /// </summary>
   4: /// <param name="pRoot"></param>
   5: /// <param name="errorItem"></param>
   6: /// <returns></returns>
   7: public static int GetErrorNumber(ILuaEngine m_Lua, PatientRoot pRoot, ErrorDict errorItem)
   8: {
   9:     if (string.IsNullOrEmpty(errorItem.Expression))  //無評分腳本
  10:     {
  11:         return 0;
  12:     }
  13:  
  14:     string m_file = System.IO.Path.Combine(LuaEx.ScriptDirectory, string.Format("emr\\error\\{0}.lua", errorItem.ErrorCode));
  15:     if (!System.IO.File.Exists(m_file))
  16:     {
  17:         return 0;
  18:     }
  19:  
  20:     try
  21:     {
  22:         m_Lua.DoFile(m_file);  //加載腳本
  23:  
  24:         object[] vs = m_Lua.Invoke("GetErrorNumber", pRoot, errorItem);  //執行函數。
  25:         if (vs != null && vs.Length > 0)
  26:         {
  27:             return int.Parse(vs[0].ToString());
  28:         }
  29:         else
  30:         {
  31:             return 0;
  32:         }
  33:     }
  34:     catch
  35:     {
  36:         return 0;
  37:     }
  38: }

這樣的好處在於,主體程序變的極其的簡單一致,不須要各類判斷和各類規則,而且靈活自動,能夠作到政策變化或者客戶需求變化導到的各類評分規則的修改,則會靈活自如。

     實踐證實,使用動態腳本語言擴展示在系統的系統是一種低投入高產出的工做,對於應對這種高擴展性和高定製性的項目是一個很是好的選擇,這個能夠解釋遊戲編程之中大量使用Lua語言的事實,針對NPC、劇情書寫相關的處理腳本,讓遊戲的後期變動,調整變得簡單可行。

4、聯繫咱們

     爲完善、改進和推廣AgileEAS.NET而成立了敏捷軟件工程實驗室,是一家研究、推廣和發展新技術,並致力於提供具備自主知識產權的業務基礎平臺軟件,以及基於業務基礎平臺開發的管理軟件的專業軟件提供商。主要業務是爲客戶提供軟件企業研發管理解決方案、企業管理軟件開發,以及相關的技術支持,管理及技術諮詢與培訓業務。

     AgileEAS.NET SOA中間件平臺自2004年秋呱呱落地一來,我就一直在逐步完善和改進,也被應用於保險、醫療、電子商務、房地產、鐵路、教育等多個應用,但一直都是以我我的在推廣,2010年由於我辭職休息,我就想到把AgileEAS.NET推向市場,讓更多的人使用。

     個人技術團隊成員都是合做多年的老朋友,由於這個平臺是免費的,因此也沒有什麼收入,都是由程序員的那種理想與信念堅持,在此我感謝一塊兒奮鬥的朋友。

團隊網站:http://www.agilelab.cn

AgileEAS.NET網站:http://www.agileeas.net

官方博客:http://eastjade.cnblogs.com

github:https://github.com/agilelab/eas

QQ:47920381

QQ羣:113723486(AgileEAS SOA 平臺)/上限1000人

199463175(AgileEAS SOA 交流)/上限1000人

212867943(AgileEAS.NET研究)/上限500人

147168308(AgileEAS.NET應用)/上限500人

172060626(深度AgileEAS.NET平臺)/上限500人

116773358(AgileEAS.NET 平臺)/上限500人

125643764(AgileEAS.NET探討)/上限500人

193486983(AgileEAS.NET 平臺)/上限500人

郵件:james@agilelab.cn,mail.james@qq.com,

電話:18629261335。

相關文章
相關標籤/搜索