制作背景

之前做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. SQL 语句与性能之执行顺序

    select * , t3.Name from t1 left join t2 on t1.sysno = t2.Asysno left join t3 on t3.sysno = t2.Bsysno ...

  2. Good Bye 2016 - B

    题目链接:http://codeforces.com/contest/750/problem/B 题意:地球的子午线长度为40000,两极点的距离为20000.现在你从北极出发,按照题目输入方式来走. ...

  3. Daily Scrum Meeting ——FifthDay(Beta)12.13

    一.Daily Scrum Meeting照片 二.Burndown Chart 三.项目进展(check-in) 1.制作注册分流的头像 发布者头像 参与者头像 2.完成参与者上传头像的功能:通过本 ...

  4. VIew-CoordinatorLayout 笔记

    CoordinatorLayout 协调者:一般会是两个控件,一个Dependency一个child ,CoordinatorLayout的主要功能就是协调这两个控件,使child跟随Dependen ...

  5. linux系统CentOS7

    linux系统CentOS7 到http://mirrors.sohu.com/mysql/下载想要的mysql版本 这里用到的是 mysql-5.6.33-linux-glibc2.5-x86_64 ...

  6. Ajax跨域问题的两种解决方法

    浏览器不允许Ajax跨站请求,所以存在Ajax跨域问题,目前主要有两种办法解决. 1.在请求页面上使用Access-Control-Allow-Origin标头. 使用如下标头可以接受全部网站请求: ...

  7. margin:0 auto;不居中

    margin:0 auto:不居中可能有以下两个的原因; 1.没有设置宽度<div style="margin:0 auto;"></div>看看上面的代码 ...

  8. DB2数据库参数建议(AIX)

    修改用户最大进程数: chdev -l sys0 -a maxuproc=' 用户资源配置:对实例用户,fence用户,应用用户添加如下限制: db2inst1 : fsize=- fsize_har ...

  9. Sublime Text 3实用快捷键大全

      下面是我通过网上教程和文本资料学习sublime Text3时收集的一些实用功能和常用快捷键,现在分享出来,如果还有其它的好用的功能可以在下面留言,以便互相学习和交流,谢谢!. 选择类 Ctrl+ ...

  10. Linux Shell 通配符、元字符、转义符【转帖】

    作者:程默 说到shell通配符(wildcard),大家在使用时候会经常用到.下面是一个实例: 1   1 2 3 4 [chengmo@localhost ~/shell]$ ls a.txt  ...