zoukankan      html  css  js  c++  java
  • JavaScript状态机程序逻辑编辑器

    制作背景

    之前做Win8 Metro动态加载内容框架的时候,由于采用了XAML+JavaScript的方法,程序复杂的执行逻辑是由JavaScript控制的,而页面一多,流程一复杂,制作起来就非常麻烦,还要考虑不同XAML页面返回事件的名称。所以就写了个状态机模型的程序制作工具。

    技术支持

    说到制作状态机,无疑微软的VS是最强大的,微软本身就有Workflow。使用微软的工作流就可以很方便的制作出一个状态机,然后在用System.Workflow下的类库提取,整理结构,然后生成JavaScript就完事了。

    使用VS来制作工作流XAML是因为VS非常直观方便,所以只使用微软工作流里的StateMachine、State、Transition、WriteLine(占位识别),支持嵌套状态机。

    技术详解

    • 解析微软工作流XAML

      首先需要引用微软工作流相关的类库System.Activities、System.Workflow.ComponentModel、System.Workflow.Activities、System.Workflow.Runtime、System.WorkflowServices。

      读取活动树,path为VS生成的工作流XAML文件路径:

      Activity act = ActivityXamlServices.Load(path);
      

      然后对活动树进行解析,活动树的类型主要处理类型:StateMachine、State(需要处理状态的Enter、Exit和Transition)、Literal`1(状态转移条件填写为True时)、CSharpValue`1(状态转移条件需要后续编程时)、WriteLine(普通预埋点)

      状态转移条件

      状态转移条件

      这一步的目的是把微软工作流的复杂对象进行整理,方便序列化给JavaScript使用,并对预埋的需要扩展JavaScript编程的地方创建JavaScript函数名(创建方法需要根据活动树生成,要保证修改工作流后原有的未更改部分生成名一致),并记录到函数表上。

      整理后的活动树

      整理后的活动树
    • JavaScript代码填写程序补完

      这一部分没什么太多说的,把之前解析出来的JavaScript函数列出来,填写上即可。当时我还是使用DataSet比较多,所以函数表使用DataSet做的,请各位想象成喜欢的形式:

      void initCodeTable()
          {
              codeTable = new DataTable("Code");
              codeTable.Columns.Add("Guid");
              codeTable.Columns.Add("Event");
              codeTable.Columns.Add("Interface");
              codeTable.Columns.Add("Codes");
              CM.Tables.Add(codeTable);
          }
      

      这里事件的概念主要是配合Win8Metro那头的框架设计的。事件都是XAML+JS Api中的。
      事件列表

      事件列表
    • JavaScript组装

      上一步已经把JavaScript函数全都做完了,接下来把这些函数生成到最终的html文件中就好了。这里预先写一个模版文件,执行主体JavaScript状态机就在这个文件里面。

      JavaScript的功底比较菜,没做封装,各位凑合看看吧:

      1 <!DOCTYPE html>
      2 
      3 <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
      4 <head>
      5     <meta charset="utf-8" />
      6     <title></title>
      7     <script type="text/javascript" src="jquery-1.8.2.min.js"></script>
      8     <script type="text/javascript" src="core-min.js"></script>
      9     <script type="text/javascript" src="enc-base64-min.js"></script>
     10     <script type="text/javascript" src="json.js"></script>
     11     <script type="text/javascript" src="UppApp.js"></script><!--UppBox通讯类库-->
     12     <script type="text/javascript" src="Network.js"></script>
     13 </head>
     14 <body>
     15     <script type="text/javascript">
     16         //省略UppBox应用层初始化
     17 
     18         var DEBUG = false;
     19         var NETDEBUG = false;
     20         var SMEngine;
     21         var currSM;
     22         var SUSPEND = false;
     23 
     24         //状态机数据注入点
     25         //<!--JSONDATA-->
     26 
     27         //状态用临时变量
     28         var tempVar = (function () {
     29             function tempVar() { }
     30             return tempVar;
     31         })();
     32 
     33         function MessageBus(para);//接收Win8Metro发来的信息,具体内容略function SendMsg(cmd, msg);//向Win8Metro发送信息,具体内容略
     34           function DebugInfo(para);//调试信息传递,可通过控制台/UppBox/body的DOM节点
     35 
     36         function InitApp() {
     37             //网络状态,配置项等的初始化注入点
     38             //<!--INITDATA-->
     39             AppDemo.initFW();//UppBox初始化
     40             SMEngine = eval(json);//json为上方注入的内容,是个Json结构对象
     41             currSM = SMEngine;
     42             if (DEBUG) DebugInfo("Init()" + currSM.DisplayName);
     43             EntryState(currSM.States[0]);
     44             try {
     45                 AppLoaded();
     46             } catch (e) {
     47             }
     48         }
     49 
     50         function runFunction(func, para) {
     51             if (func == null) return false;
     52             switch (func.Type) {
     53                 case "Action":
     54                     var funName = func.Code;
     55                     try {
     56                         if (DEBUG) DebugInfo("runFunction():" + funName + " para:" + para);
     57                         var result = eval(funName + "(para);");
     58                         return result;
     59                     } catch (e) {
     60                         return false;
     61                     }
     62                     break;
     63 
     64                 default:
     65                     break;
     66             }
     67         }
     68 
     69         function TAction(action, entryState, para) {
     70             if (DEBUG) DebugInfo("TAction:" + action.DisplayName + " " + entryState.DisplayName + " " + para);
     71             try {
     72                 if (para != "[SUSPEND]") {
     73                     var result = runFunction(action, para);
     74                     if (result == false) {
     75                         SUSPEND = true;
     76                         setTimeout(function (action, entryState) {
     77                             TAction(action, entryState, "[SUSPEND]");
     78                         }, 500, action, entryState);
     79                     } else {
     80                         EntryState(entryState);
     81                     }
     82                 }
     83                 else {
     84                     if (SUSPEND == false) {
     85                         EntryState(entryState);
     86                     } else {
     87                         setTimeout(function (action, entryState) {
     88                             TAction(action, entryState, "[SUSPEND]");
     89                         }, 500, action, entryState);
     90                     }
     91                 }
     92             } catch (e) {
     93             }
     94         }
     95 
     96         function Trigger(para) {
     97             if (SUSPEND) {
     98                 if (DEBUG) DebugInfo("SUSPEND():" + para);
     99                 otherFun(para);
    100                 return;
    101             }
    102             for (var i = 0; i < currSM.currState.Transitions.length; i++) {
    103                 var result = runFunction(currSM.currState.Transitions[i].Trigger, para);
    104                 if (result == true) {
    105                     var result = runFunction(currSM.currState.Transitions[i].Condition, para);
    106                     if (result == true) {
    107                         var stateName = currSM.currState.Transitions[i].To;
    108                         for (var j = 0; j < currSM.States.length; j++) {
    109                             var sns = currSM.States[j].DisplayName.split("_");
    110                             var CurrName = sns[sns.length - 1];
    111                             if (CurrName == stateName) {
    112                                 ExitState();
    113                                 TAction(currSM.currState.Transitions[i].Action, currSM.States[j], para);
    114                                 return;
    115                             }
    116                         }
    117                         return;
    118                     }
    119                 }
    120             }
    121             var result = runFunction(currSM.currState.Main, para);
    122             if (result != true) {
    123                 if (DEBUG) DebugInfo("otherFun(): para:" + para);
    124                 otherFun(para);
    125             }
    126         }
    127         
    128         function otherFun(para) {
    129             try {
    130                 var str = "";
    131                 var msg = "";
    132                 try {
    133                     str = para.split(" ");
    134                     if (str.length > 1) msg = str[1];
    135                     for (var i = 2; i < str.length; i++) {
    136                         msg += " " + str[i];
    137                     }
    138                     eval(str[0] + "(msg)");
    139                 } catch (e) {
    140                     str = para;
    141                     eval(str + "()");
    142                 }
    143             } catch (e) {
    144             }
    145         }
    146 
    147         function EntryState(state) {
    148             if (DEBUG) {
    149                 var debug = "EntryState():" + state.DisplayName;
    150                 if (currSM.currState != null) debug += " CurrState:" + currSM.currState.DisplayName;
    151                 DebugInfo(debug);
    152             }
    153             switch (state.Entry.Type) {
    154                 case "Action":
    155                     if (!state.IsFinal) {
    156                         currSM.currState = state;
    157                     } else {
    158                         while (true) {
    159                             if (currSM.preSM == null) break;
    160                             currSM = currSM.preSM;
    161                             if (!currSM.currState.IsFinal) break;
    162                         }
    163                     }
    164                     runFunction(state.Entry, null);
    165                     Trigger("");
    166                     break;
    167 
    168                 case "StateMachine":
    169                     currSM.currState = state;
    170                     state.Entry.preSM = currSM;
    171                     currSM = state.Entry;
    172                     EntryState(currSM.States[0]);
    173                     break;
    174 
    175                 default:
    176                     break;
    177             }
    178         }
    179 
    180         function ExitState() {
    181             if (DEBUG) DebugInfo("ExitState():" + currSM.currState.DisplayName);
    182             if (currSM.currState != null) {
    183                 runFunction(currSM.currState.Exit, null);
    184             }
    185         }
    186 
    187         //函数注入点,编辑器中写的各种函数在此注入
    188         //<!--FUNCDATA-->
    189 
    190         //举个例子
    191         //Entry:SM_23717574ae2bff0c6e0984140518db_欢迎界面初始化_Entry_初始化_Entry:
    192         function A_87846c21b09faea4a622f8331aa9a4(para){
    193             var str;
    194             try {
    195                 str = para.split(" ");
    196                 switch (str[0]) {
    197                     default:
    198                     break;
    199                 }
    200             }catch(e){}
    201 
    202             SendMsg("Storyboard", "[NULL] Start 霓虹灯");
    203             SendMsg("Storyboard", "[NULL] Start 进入");
    204             SendMsg("Storyboard", "[NULL] Start 眨眼睛");
    205         }
    206     </script>
    207 </body>
    208 </html>
    View Code

    其他

    程序逻辑编辑器界面

    程序逻辑编辑器界面

    程序逻辑编辑器界面

    程序逻辑编辑器界面

    程序逻辑编辑器界面
    HTML5支持WebSocket可以连接UppBox,自写的JS状态机引擎在调试模式下可以把与C#代码交互过程全部传输出来,还可以传输指令,方便调试。

    作者:SuperEVO
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    计算机网络知识
    数据库知识
    操作系统知识
    计算机硬件基础知识
    计算机科学基础知识
    2019下半年软件设计师考试大纲
    软件设计师补题(2008下半年上午题)
    软件设计师补题(2008上半年上午题)
    测试复盘3
    测试复盘2
  • 原文地址:https://www.cnblogs.com/zhang740/p/3716291.html
Copyright © 2011-2022 走看看