1. 项目简介
1.1. 项目简述
- 规则可视化引擎的主要目标是对regchain平台的规则进行可视化,主要目标是生成可读性高,美化程度好的可视化图像。
- 规则可视化编辑的目标是创建可视化编辑引擎,能够做到通过生成可视化图像以及通过图像和节点的拖拉生成可以部署于regchain应用上的规则引擎。
1.2. 可视化规则及编辑引擎流程
- 通过点击Regchain-Explorer的Rule界面的规则文件,可以跳转到规则可视化的网站。
- 这个过程中,规则文件的URIEncode网址的Rule参数被传入规则可视化网站,其中一个样例为链接。
- 注意,如果在网址框中输入之后没有反应,可以通过再次刷新完成进入页面。
- 其中,网址中rule代码的原文案例为下文代码,可以通过F12中浏览器命令行输入
encodeURI'你的代码')
完成编码。
regulation hello_worldentities {entity Hello {World}
}rules {rule r1 {Hello.World >= 0}
}
- 完成可视化的图像样例如下图所示:
1.2.1. 界面以及可以进行的操作
1.2.1.1. 界面整体概览
- 左边是文件浏览框,内部有工作区,规则文件名和单个规则。
- 右侧是可视化图形框/编辑框,上方是导航,下方是文本编辑框。
1.2.1.2. 操作
- 每次新打开一个网页就会新建一个工作区,工作区默认格式名为:
newWorkspace_{No.}
。 - 工作区下方就是文件规则名,文件规则名下方是不同的规则。
- 每点击一个规则,右侧就会出现一个Tab窗口,代表着这个规则所对应的可视化图像。
- 在可视化图像上,你可以对节点进行拖拽,赋值,放大及缩小。
- 能在节点之间进行连线。
- 你可以通过点击导航上不同的tab切换不同的规则。
- 下方的文本编辑栏中的规则就代表着当前所处的规则文件。TODO)
- 在编辑栏中按下Ctrl+Enter就可以对规则代码进行可视化。TODO)
- 键盘键入
tab
键,可视化编辑栏中心会出现一个小型的编辑框,在编辑框中输入文字即可搜索节点,例如键入“>=”,则中心的筛选框中出现">="节点。模仿blender),此时键入"Enter"即可在编辑器中显示节点。TODO) - 可以自由的拖动规则,链接连线,下方的文本会自动进行更新。TODO)
- 可以通过左侧的这个按钮新建规则。TODO)
- 也可以通过这个按钮新建规则文件。TODO)
1.3. 规则可视化及编辑整体技术思路
1.3.1. 规则可视化框架思路
- 这幅图描述了可视化的基本流程。
- Antlr4方向生成的语法书可以供树上遍历时做参考。
- 生成的语法树如下图所示:
- 通过Antlr4生成的语法树可以供我们在编写算法时作参考,但是无法作为前端实时编译遍历的资源。下面会介绍如何用Antlr4编译出语法树)。
- Antlr4ts编译生成是可供遍历的代码文件,我们通过引入
AbstractParseTreeVisitor
来获取语法树上的节点和连接信息。 - 我们在遍历语法树之前在前端界面上生成react-diagram的画布,在我们遍历的同时,我们把树上的信息和我们要进行的操作打入一个虚拟栈中。
- 这个虚拟栈保存了我们遍历这个树的节点和和我们要进行的动作;建立这个虚拟栈的原因是我们不知道树上的可视化节点生成位置。因为你在绘制某个树节点的同时不知道后续节点的信息,你无法确定当前这个节点的位置[1]。
- 这个时候需要通过第一轮遍历获取树的信息后,通过请求
pymag-trees
的API,获取树的位置。 pymag-trees
的代码已经在项目中重写,已经将Python2的部分改写成了python3。
1.3.2. 规则可视化编辑框架整体思路
- 总的来说规则可视化编辑的思路要比可视化的思路简单。
- 他主要通过定义每个节点的代码片段还原代码原来的功能,例如递归到">=“这个节点,则会被描述成”
片段1
>=
片段2
"。[2] - 然后我们递归进入
片段1
,片段1
则会重复执行一遍递归的代码,通过自适应选择代码片段,它会进入合适的函数。 - 如果这个函数会进一步递归,那么重复上面的步骤;如果不会,就直接返回节点所代表的值。
- 下面通过一个案例做一个演示
- 首先从根节点开始递归,生成规则周围的代码,目前的代码块为
rules {rule r1 {}
}
- 然后进入到
大于等于
节点,此时返回一个大于等于号,执行的伪代码如下
mid = ">="
left = recurnode_in_1)
right = recurnode_in_2)
return left+" "+mid+" "+right
- 在
node_in_1
中获得了变量Hello.World
,则直接返回,此时left=Hello.World
。 - 同理
right
就直接等于0
- 所以最后的代码块为:
rules {rule r1 {Hello.World >= 0}
}
1.4. 使用框架介绍
1.4.1. React
- 流行的前端框架,语法和相关概念参见官方文档和教程。
1.4.2. Antlr4
- 用于定义Domain Specific Language语法的工具,可以通过其生成具有完整功能的语法树。
- 它的组成部分主要有g4文件,示例代码文件。
- g4文件主要定义的是语法树不同的切割规则和操作符。
- 它在mac快速部署的方案可以参考antlr4官网。
- windows的部署相对麻烦一些,主要可以参考antlr4手册。
1.4.3. Antlr4ts
- 全称是antlr4 typescript版本。
- 主要是生成typescript可以使用的库文件。
- 因为目前react主要是使用typescript作为开发语言,Antlr4ts可以自动的生成相关的visitor类库,以供语法树的遍历。
- antlr4ts有它关于visitor遍历特定的语法,具体可以参考他们github主页。
- 目前项目中的按antlr4对于visitor的使用也比较规范,参考当前项目也是可以的。
1.4.4. React-Diagram
- 一个仿Blender,Unreal Blueprint引擎的图形化前端API接口集。
- 它的画布允许默认的节点绘制,节点拖动,分层,接口连接等功能。
- 它的API允许添加各种颜色的节点,为节点命名,修改节点位置,序列化节点信息,添加接口,为接口命名,添加连线,自动分层,并预留了复制,点击等各种事件的实现接口。
- 最好的入门资料来自于最基础的React-Diagram+Antlr4项目,以及他们的官方项目。
- 阅读官方项目的gallery对理解整个项目的运作方式很有帮助。
1.4.5. Remix-Project
- 由于我们可视化编辑的项目非常类似于在线编辑Solidity,并编译部署上链的设计,所以我们选用remix-project作为我们整个可视化最底层的框架。
- 从他的视觉展示功能的角度,他们具有大型的编辑面板,具有文件浏览器,这都是我们需要的功能。
- 不仅如此,它对编辑器有所拓展,只要注册它的组件,就能够模仿编辑器的切换tab,保存过去信息的功能。
- 这个项目以javascript为主,这导致融合antlr4ts存在困难,这一个部分可以通过特殊的融合方法进行处理。
- remix-project还有一种特殊的持久化存储系统,在初次上手的时候相对难以理解。
- 上面有对remix-project的基本界面信息做介绍,在下面的部分会对remix-project整体的项目做一个介绍。
1.5. 目前项目启动方法
1.5.1. 安装配置rule-visualization-web
npm install -g @nrwl/cli
git clone https://github.com/RegTech-OXH/rule-visualization-web.git
cd rule-visualization-web
npm install
nx build remix-ide --with-deps
nx serve
- 访问:
localhost:8000
。 - 还需要启动n元树绘制位置的后台服务)
可能遇到的问题
- 需要先安装
node
,目前版本为16.6.1,windows,mac以及ubuntu都可。 npm
版本为7.20.3
。- 必要的运行库
apt-get install build-essential
。
1.5.2. n元树绘制后台服务
git clone https://github.com/MrWater98/pymag-trees.git
- 自行安装
flask
和flask_cors
库 python3 service.py
- 成功标志:
测试网址
http://localhost:8090/#optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.4+commit.c7e474f2.js&rule=regulation%20hello_world%0A%0Aentities%20%7B%0A%20%20%20%20entity%20Hello%20%7B%0A%20%20%20%20%20%20%20%20World%0A%20%20%20%20%7D%0A%7D%0A%0Arules%20%7B%0A%20%20%20%20rule%20r1%20%7B%0A%20%20%20%20%20%20%20%20Hello.World%20%3E%3D%200%0A%20%20%20%20%7D%0A%7D
2. Rule-Visualization-Web 项目核心框架
2.1. 框架架构图
- 注1: Node fs为Node中对其所在文件系统进行封装的特殊文件系统。
- 住2:remix注册组件以及remix事务来自于remix原生的事务管理库。
2.2. 框架修改关键位置解析
- 整个框架的入口位置为
app.js
。 app.js
引入了file-panel.js
组件,它通过URL参数获得了规则文件信息。- 然后
file-panel.js
将规则文件信息交给ruleInit.tsx
,此时可以对规则文件进行一个初筛,获取分开注释的单个规则——这用于将一个规则文件的多个规则分开。 - 然后通过初始化工作区的过程,将信息交给
filManager.js
。 fileManager.js
把信息传递给editor.js
,然后editor.js
通过ParserAlg
,利用React的渲染机制,把自己editor的界面交给RuleParser.tsx
进行渲染。RuleParser.tsx
根据之前获取的所有文件信息,在通过8802端口中的位置信息进行画面的渲染。
3. RuleParser算法与数据结构解析
3.1. Antlr4ts的安装和配置
npm install antlr4ts --save
npm install antlr4ts-cli --save-dev
- 将
g4
文件放入apps\remix-ide\src\app\ruleparser\antlrScript
可以自选位置)。 - 在
package.json
的scripts
加入下面这行
"generate-visitor": "antlr4ts -o apps/remix-ide/src/app/ruleparser/_g4_script -visitor apps/remix-ide/src/app/ruleparser/antlrScript/RuleParser.g4"
- 运行
npm run generate-visitor
- 运行成功后尝试引入
import { RuleParserVisitor } from "./_g4_script/RuleParserVisitor";
,引入成功即为Antlr4ts安装成功。
3.2. Visitor基本工作原理
- 首先如果要用visitor遍历语法树,那么则需要实现
RuleParserVisitor<object>
的接口,其中object
这里意味着每个树节点返回上一个节点的类型属性,你也可以修改成string或者其他的类型。 - 通过实际的语法树来理一遍visitor的访问顺序:
- 这个是
hello_world
实际的语法树文件,上面关于antlr4配置生成语法树的链接有很完整的介绍:就是要注意在windows中,终止语法树读取的快捷键是Ctrl+Z
外加回车。 - 对于
visitor
而言,root为遍历的入口,那么遍历这个root就需要一个实现visitRoot
,我们需要遍历它的每个子节点,那么就需要通过一个for循环,访问它的每个孩子this.visitcontext.getChildi));
,那么这个函数就会写成:
visitRootcontext: RootContext){forlet i = 0;i < context.childCount;++i){this.visitcontext.getChildi));}return {};}
- 继续,当我我们要访问
ruleBlocks
的时候,我们就需要实现visitRuleBlocks
,在visitRuleBlocks
中我们可以实现我们接下来的逻辑。
/** Note: Visit `ruleBlocks` node* Assumption: ruleList is the third child*/visitRulesBlockcontext:RulesBlockContext){this.visitcontext.getChild2));return {};}
- 当然,每个节点有它当时设定的一些前提,我们要利用这一些前提对访问进行构造。
4. 可视化绘制原理
4.1. 算法流程描述
- 首先,算法从遍历树节点开始,在会渲染的节点增加层数和构建连接情况,把这些存储在一个叫
treeStr
的字符串中。 - 一个
treeStr
的例子是:Tree"root",Tree"m",Tree"leave"),Tree"leave")))
,Tree的第一个参数为参数名,第二个参数为子树,这是一个递归结构的字符串,这个结构将会被发送到后台的service.py解析并返回节点位置,上面这个字符串返回的节点位置为:
finished v = m children
shift: leave: x=1.0 mod=0 0 0
shift: leave: x=0.0 mod=0 0 0
finished v = root children
shift: m: x=0.5 mod=0 0 0
- 同理,遍历树节点时,将节点的数据存储的
tree_instr
如下图所示,每个tree_instr的元素为一个object,有着key和value:
- 它的遍历方式类似访问一个类似一个栈,渲染一个节点的时候总会和栈顶节点作连线。
- 每一个不同的key的渲染方式都是不同的。
4.2. n元树绘制优化算法演示
- [1]
4.3. 树指令算法tree_instr)演示TODO)
- 首先,声明一个栈
St
,作为上一个遍历的位置。 - 首先遍历这个数组,第0位为
render_rule
,则在画布上渲染一个rule节点;- 然后检索
St
的栈顶,发现没有元素,不做操作。 - 跟着往
St
中推入这个节点。
- 然后检索
- 然后遍历第1位,发现是
render_rs3
,代表着有三个子节点语法树种)的RelationExpr
,在画布上渲染一个Relation
节点。- 然后检索栈顶,发现栈顶为
rule
的节点,则将其相连。 - 往
St
中推入这个节点。
- 然后检索栈顶,发现栈顶为
- 遍历第2位,发现
instr
且指令不是out
,忽略; - 遍历第3位,发现
reander_ea
,代表着entity.attributeName
节点,在画布上渲染一个entity.attributeName
节点。- 检索栈顶,发现是
RelationExpr
的节点,将其相连。 - 往
St
中推入这个节点。
- 检索栈顶,发现是
- 遍历第4位,发现是
out
,推出栈顶节点。 - 以下略
5. 可视化编辑系统TODO)
参考文献:
[1] https://llimllib.github.io/pymag-trees/
[2] https://developers.google.com/blockly