制作背景

之前做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. 主机ping不同虚拟机

    前提:今天加班时,一不小时把网线踢掉了,然而虚拟机怎么都ping不通了,试过很多办法; 最后.... 简单粗暴的重启了我的主机,对,是主机,不是虚拟机,ok,问题解决; over...

  2. 学习微信小程序之css12设置盒子内容的宽高

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. 【vuejs小项目】一、脚手架搭建工作

    一.关于vuejs 这是一个MVVM的前端开发框架,model(数据).viewmode(通讯).view(视图),它吸取了reactjs和angularjs的长处,核心重点在于组件化的设计原则. 我 ...

  4. LINUX 忘记root密码如何修改

    重启linux系统 3 秒之内要按一下回车,出现如下界面 然后输入e 在 第二行最后边输入 single,有一个空格.具体方法为按向下尖头移动到第二行,按"e"进入编辑模式 在后边 ...

  5. 【转】OBJECT_ID和DATA_OBJECT_ID的区别

    在user_objects等视图里面有两个比较容易搞混的字段object_id和data_object_id这两个字段基本上有什么大的区别呢?object_id其实是对每个数据库中数据对象的唯一标识d ...

  6. SpringMVC -- 注解

    @Entity -- 实体类@Table(name = "hat_province", catalog = "news") -- 对应的表name -- 表名c ...

  7. JS操作Json

    因为我水啊 所以我就要手打一下 熟悉一下 ===== JSON 全称 JavaScript Object Notation(标记) 一种轻量级的数据交互格式,采用完全独立于语言的文本格式 同事JSON ...

  8. Linux下添加apache虚拟主机

    一切在确保apache被正确安装的前提下 设置虚拟主机 创建虚拟目录 现在,让我们继续安装虚拟主机.虚拟主机命名为local.gis. 创建一个公用的文件夹来存放这虚拟主机的数据. 首先,让我们为lo ...

  9. postman 断言解析

    最近在学习postman官方文档, 顺势翻译出来,以供学习! postman断言是JavaScript语言编写的,在postman客户端指定区域编写即可. 断言会在请求返回之后,运行,并根据断言的pa ...

  10. Yii框架 400 错误

    YII  400错误 在YII框架中400错误是csrf校验失败的意思 csrf是什么? CSRF(Cross-site request forgery跨站请求伪造,也被称为"One Cli ...