制作背景

之前做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的功底比较菜,没做封装,各位凑合看看吧:

 <!DOCTYPE html>

 <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript" src="jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="core-min.js"></script>
<script type="text/javascript" src="enc-base64-min.js"></script>
<script type="text/javascript" src="json.js"></script>
<script type="text/javascript" src="UppApp.js"></script><!--UppBox通讯类库-->
<script type="text/javascript" src="Network.js"></script>
</head>
<body>
<script type="text/javascript">
//省略UppBox应用层初始化 var DEBUG = false;
var NETDEBUG = false;
var SMEngine;
var currSM;
var SUSPEND = false; //状态机数据注入点
//<!--JSONDATA--> //状态用临时变量
var tempVar = (function () {
function tempVar() { }
return tempVar;
})(); function MessageBus(para);//接收Win8Metro发来的信息,具体内容略function SendMsg(cmd, msg);//向Win8Metro发送信息,具体内容略
function DebugInfo(para);//调试信息传递,可通过控制台/UppBox/body的DOM节点 function InitApp() {
//网络状态,配置项等的初始化注入点
//<!--INITDATA-->
AppDemo.initFW();//UppBox初始化
SMEngine = eval(json);//json为上方注入的内容,是个Json结构对象
currSM = SMEngine;
if (DEBUG) DebugInfo("Init()" + currSM.DisplayName);
EntryState(currSM.States[0]);
try {
AppLoaded();
} catch (e) {
}
} function runFunction(func, para) {
if (func == null) return false;
switch (func.Type) {
case "Action":
var funName = func.Code;
try {
if (DEBUG) DebugInfo("runFunction():" + funName + " para:" + para);
var result = eval(funName + "(para);");
return result;
} catch (e) {
return false;
}
break; default:
break;
}
} function TAction(action, entryState, para) {
if (DEBUG) DebugInfo("TAction:" + action.DisplayName + " " + entryState.DisplayName + " " + para);
try {
if (para != "[SUSPEND]") {
var result = runFunction(action, para);
if (result == false) {
SUSPEND = true;
setTimeout(function (action, entryState) {
TAction(action, entryState, "[SUSPEND]");
}, 500, action, entryState);
} else {
EntryState(entryState);
}
}
else {
if (SUSPEND == false) {
EntryState(entryState);
} else {
setTimeout(function (action, entryState) {
TAction(action, entryState, "[SUSPEND]");
}, 500, action, entryState);
}
}
} catch (e) {
}
} function Trigger(para) {
if (SUSPEND) {
if (DEBUG) DebugInfo("SUSPEND():" + para);
otherFun(para);
return;
}
for (var i = 0; i < currSM.currState.Transitions.length; i++) {
var result = runFunction(currSM.currState.Transitions[i].Trigger, para);
if (result == true) {
var result = runFunction(currSM.currState.Transitions[i].Condition, para);
if (result == true) {
var stateName = currSM.currState.Transitions[i].To;
for (var j = 0; j < currSM.States.length; j++) {
var sns = currSM.States[j].DisplayName.split("_");
var CurrName = sns[sns.length - 1];
if (CurrName == stateName) {
ExitState();
TAction(currSM.currState.Transitions[i].Action, currSM.States[j], para);
return;
}
}
return;
}
}
}
var result = runFunction(currSM.currState.Main, para);
if (result != true) {
if (DEBUG) DebugInfo("otherFun(): para:" + para);
otherFun(para);
}
} function otherFun(para) {
try {
var str = "";
var msg = "";
try {
str = para.split(" ");
if (str.length > 1) msg = str[1];
for (var i = 2; i < str.length; i++) {
msg += " " + str[i];
}
eval(str[0] + "(msg)");
} catch (e) {
str = para;
eval(str + "()");
}
} catch (e) {
}
} function EntryState(state) {
if (DEBUG) {
var debug = "EntryState():" + state.DisplayName;
if (currSM.currState != null) debug += " CurrState:" + currSM.currState.DisplayName;
DebugInfo(debug);
}
switch (state.Entry.Type) {
case "Action":
if (!state.IsFinal) {
currSM.currState = state;
} else {
while (true) {
if (currSM.preSM == null) break;
currSM = currSM.preSM;
if (!currSM.currState.IsFinal) break;
}
}
runFunction(state.Entry, null);
Trigger("");
break; case "StateMachine":
currSM.currState = state;
state.Entry.preSM = currSM;
currSM = state.Entry;
EntryState(currSM.States[0]);
break; default:
break;
}
} function ExitState() {
if (DEBUG) DebugInfo("ExitState():" + currSM.currState.DisplayName);
if (currSM.currState != null) {
runFunction(currSM.currState.Exit, null);
}
} //函数注入点,编辑器中写的各种函数在此注入
//<!--FUNCDATA--> //举个例子
//Entry:SM_23717574ae2bff0c6e0984140518db_欢迎界面初始化_Entry_初始化_Entry:
function A_87846c21b09faea4a622f8331aa9a4(para){
var str;
try {
str = para.split(" ");
switch (str[0]) {
default:
break;
}
}catch(e){} SendMsg("Storyboard", "[NULL] Start 霓虹灯");
SendMsg("Storyboard", "[NULL] Start 进入");
SendMsg("Storyboard", "[NULL] Start 眨眼睛");
}
</script>
</body>
</html>

其他

程序逻辑编辑器界面

程序逻辑编辑器界面



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

JavaScript状态机程序逻辑编辑器的更多相关文章

  1. 了解 JavaScript 应用程序中的内存泄漏

    简介 当处理 JavaScript 这样的脚本语言时,很容易忘记每个对象.类.字符串.数字和方法都需要分配和保留内存.语言和运行时的垃圾回收器隐藏了内存分配和释放的具体细节. 许多功能无需考虑内存管理 ...

  2. 大型JavaScript应用程序架构模式

    11月中旬在伦敦举行的jQuery Summit顶级大会上有个session讲的是大型JavaScript应用程序架构,看完PPT以后觉得甚是不错,于是整理一下发给大家共勉. PDF版的PPT下载地址 ...

  3. Backbone.js 为复杂Javascript应用程序提供模型(models)、集合(collections)、视图(views)的结构

    Backbone.js 为复杂Javascript应用程序提供模型(models).集合(collections).视图(views)的结构.其中模型用于绑定键值数据和 自定义事件:集合附有可枚举函数 ...

  4. (6)javascript的程序控制结构及语句-----(1)条件判断

    程序控制结构及语句 编程就是将现实应用,转换为程序能够读得懂的语法语句.Javascript编程中对程序流程控制主要是通过条件判断语句.循环控制语句及continue.break来完成的,其中条件判断 ...

  5. CTF---Web入门第十二题 程序逻辑问题

    程序逻辑问题分值:20 来源: 实验吧 难度:中 参与人数:6909人 Get Flag:1993人 答题人数:2070人 解题通过率:96% 绕过 解题链接: http://ctf5.shiyanb ...

  6. Java Web安全之程序逻辑缺陷

    Java Web程序逻辑缺陷本质是由于程序设计和开发者设计的程序执行逻辑存在某种缺陷而导致的安全隐患.企业的代码审查和渗透测试通常主要针对的大多是诸如xss攻击和sql注入和跨站点脚本这些头条式漏洞, ...

  7. javascript状态机及在工作流中的应用

    #javascript状态机及在工作流中的应用 ##状态机 什么叫状态机(Finite State Machine),书面上的解释可以自己借助搜索引擎寻找到.通俗地来讲是一个状态定义.查找.切换和事件 ...

  8. 《JavaScript》 程序基本知识 数据类型。 {0912上} {0912下}

    JS脚本语言: 这是JaxaScript的全称名 JS是网页里面使用的脚本语言 JS是一个非常强大的语言 JS的基础语法 注释语法:  单行注释 //     多行注释 /**/ 输出语法:   双标 ...

  9. JavaScript的程序构成

    JavaScript的程序构成主要从以下几方面做个总结:控制语句.函数 .事件驱动及事件处理,希望对读者有些帮助. 控制语句: if条件语句 基本格式 if(表述式) 语句段1: ...... els ...

随机推荐

  1. 21分钟 MySQL 入门教程(转载!!!)

    21分钟 MySQL 入门教程 目录 一.MySQL的相关概念介绍 二.Windows下MySQL的配置 配置步骤 MySQL服务的启动.停止与卸载 三.MySQL脚本的基本组成 四.MySQL中的数 ...

  2. 初识node.js

    Node.js不是一种语言:不是框架:也不是工具.它是用于运行基于JavaScript应用程序的运行时环境.

  3. Java内存模型及性能优化

    最近在做一个项目的性能优化,遇到好多以前没有关注过的性能问题,一头雾水,今天做个笔记,简单记录下JVM相关的参数设置. 一.JVM内存模型 首先介绍下Java程序具体执行的过程: Java源代码文件( ...

  4. nodejs复习05

    stream 可读流 fs.pause()方法会使处于流动模式的流停止触发data事件,切换到非流动模式并让后续数据流在内部缓冲区 var fs = require('fs') var rs = fs ...

  5. eclipse安装hibernate-Tools

    启动eclipse 选择Help -> About Eclipse 记住自己的eclipse版本 访问http://download.jboss.org/jbosstools/updates/s ...

  6. 51Nod 1010 只包含因子2 3 5的数 Label:None

    K的因子中只包含2 3 5.满足条件的前10个数是:2,3,4,5,6,8,9,10,12,15. 所有这样的K组成了一个序列S,现在给出一个数n,求S中 >= 给定数的最小的数. 例如:n = ...

  7. http://devdocs.io/【文档收藏】

    http://devdocs.io http://bower.io/ www.bower.iobrowserify.org jsPlumb布局 https://github.com/lndb/jsPl ...

  8. 第三章 一个简单的机器学习例子让你了解DeepLab的语言风格

    MINST是由Yann LeCun等人建立并维护的手写数字识别数据库.该数据库总共包含60000个训练样本和10000个测试样本.其中每个样本的大小是一张28*28的手写数字图片.数字包括从0~9总共 ...

  9. PS特效精粹

    冲击有力的广告效果 神奇画笔功能 + 强大的图层样式 Photoshop中的三维特效

  10. 使用canal分析binlog(二) canal源码分析

    在能够跑通example后有几个疑问 1. canal的server端对于已经读取的binlog,client已经ack的position,是否持久化,保存在哪里 2. 即使不启动zookeeper, ...