页面树结构

版本比较

标识

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。

问题

一个夏天的下午,负责大数据分析的算法工程师小伍来找我,问:“我精心研究了一个车辆载荷分析算法,其中用到了那个'基于#@$%%^$的模型',原理是这样的,@##$%^#$%^#@$#%@#。我在笔记本上用Python写了个脚本试了一下,感觉还不错,但是我想把这些算法和DWF上管理的车辆信息管理的界面对接起来,用户选择一个车辆,然后点击分析,后端启动我的脚本同时传入车号的信息,脚本运行好以后把分析结果写回到DWF的数据库里,一刷新就把结果展示出来,怎么办呢?哦,对了,如果可能的话,可以把我的Python脚本也管理起来,让以后我只要选择算法就更好了”。

解法

我看了看,说:“不难,你需要的是在后端写点脚本就可以完成这个任务了。利用下面的几个DWF提供的调用即可实现目标:”

启动Python脚本并传参

this.sh.execute

这个后端脚本的函数,可以直接启动python脚本,参数为一个字符串,将完整的python脚本路径输入后即可启动执行,例如,假设服务器上有一个名为analytic.py的脚本在/home/scripts路径下,那么调用可以这么写:

...

代码块
languagepy
import sys
...
def main():
    if len(sys.argv) > 1 :
        oid = sys.argv[1]
...

如果希望在python脚本里简单的输出一些数据给dwf的后端javascript脚本,可以使用下面的写法:

代码块
languagejs
let scriptPath = "/home/script/analytic.py";
let oid = this.obj.oid;
let r = this.sh.execute(`python ${scriptPath} ${oid}`);

而在对应的python脚本里通过print函数可以将结果返回即可:

代码块
languagejs
import sys

if len(sys.argv) > 1 :
    print("hello " + sys.argv[1])

最后一点要注意的是,如果希望从python脚本里传递非常复杂的JSON对象,应该用json包的dumps实现:print(json.dumps(res))

以文件为基础输入和输出数据

“OK,那我怎么拿到DWF中管理的文件路径呢?”,说到这里,小伍问到。

好问题,这件事情不难,DWF在后端是已经准备好了接口实现这个目标,如果你在DWF实体类或者关联类上绑定了LocalFile类型的属性,那么文件可以作为附件上传到后端,然后就用下面的两个函数。

this.omf.getFilePath

这个函数传入一个对象的oid,DWF中类的英文名以及LocalFile类型属性的属性英文名名,如果在后端的能找到文件,那么DWF会把这个文件的名字以字符串的方式返回,启动python脚本的时候作为启动参数传递进去即可。

...

代码块
languagejs
let scriptPath = this.omf.getFilePath(this.obj.modelOid, "AnalyicModel","modelFile");
let oid = this.obj.oid;
let dataPath = this.omf.getFilePath(oid, "AnalyticTask", "dataFile"); 

// 要检查一下文件是否存在
let Files = Java.type('java.nio.file.Files');
let Paths = Java.type('java.nio.file.Paths');
if (Files.exist(Paths.get(dataPath)){
	this.sh.execute(`python ${scriptPath} ${oid} ${dataPath }`); 
}

this.omf.setFilePath

如果希望将结果输出并且保存到DWF里,那么可以在启动python脚本的时候输入一个目标输出文件,将这个临时文件的路径也作为脚本的启动参数传入,由Python脚本输出以后在调用DWF后端的this.omf.setLocalFile即可将结果文件附加到指定的属性上。

...

代码块
languagejs
let scriptPath = "/home/script/analytic.py";
let oid = this.obj.oid;
let dataPath = this.omf.getFilePath(oid, "AnalyticTask", "dataFile"); //假设dataFile属性为一个绝对路径

// 要检查一下文件是否存在
let Files = Java.type('java.nio.file.Files');
let Paths = Java.type('java.nio.file.Paths');
let File = Java.type('java.io.File');
if (Files.exist(Paths.get(dataPath)){
	// 生成一个后缀为tmp的临时文件
	let resultFileresultPath = File.createTempFile(oid, 'tmp');
	this.sh.execute(`python ${scriptPath} ${oid} ${dataPath} ${restulPathresultPath}`);
	this.omf.setLocalFile(oid, "AnalyticTask", "resultFile", resultFileresultPath);
	// JDK在退出的时候删除临时文件
    resultFile.deleteOnExit()
}

以数据库为基础输入和输出数据

“嗯,还不错,这样我可以去搭建一个原型系统了,等等!可能还是有问题!”,小伍眼睛一转,紧接着问到:

...

不过有一点得注意一下,如果数据分析涉及到的数据量很大,时效性要求又很高,那就还是得用一个专门的大数据计算集群完成这个工作,比如:大数据系统软件国家工程实验室的Flok项目就是一个这样的系统。

示例数据模型

  • 车辆信息(Asset):用于记录车辆的基本信息,例如:车号,车型,业主名称等,简化一点这里就提供了一个设备类型和设备名称的属性。
  • 分析模型(AnalyticModel):用于存放车辆分析算法的python脚本。
  • 分析任务(AnalysisTask):每次分析的任务对应的是分析结果记录,其中可以允许用户上传一个数据文件,在正式的生产系统里可以直接把数据对接到某个数据库,例如:IoTDB

...

可以通过4.6 模型包管理直接导入将上述模型导入到空白的DWF之中。

尾声

说话之间,已经到了傍晚,只见万道霞光照进办公室,小伍看了看窗外,感觉信心十足,答到:“哦,原来是这样的,好!我这就回去试试!”。说话之间,已经到了傍晚,只见霞光照进办公室,小伍看了看窗外,感觉信心十足,答到:“哦,原来是这样的,好!我这就回去试试!”。

“别急!”,我说:“先开完组会再说,马上就要开始了。”,只听熟悉的声音响起,全组开始汇报大家开始汇报工作,晚上继续工作。

...