问题
一个夏天的下午,负责大数据分析的算法工程师小伍来找我,问:“我精心研究了一个车辆载荷分析算法,其中用到了那个'基于#@$%%^$的模型',原理是这样的,@##$%^#$%^#@$#%@#。我在笔记本上用Python写了个脚本试了一下,感觉还不错,但是我想把这些算法和DWF上管理的车辆信息管理的界面对接起来,用户选择一个车辆,然后点击分析,后端启动我的脚本同时传入车号的信息,脚本运行好以后把分析结果写回到DWF的数据库里,一刷新就把结果展示出来,怎么办呢?哦,对了,如果可能的话,可以把我的Python脚本也管理起来,让以后我只要选择算法就更好了”。
解法
我看了看,说:“不难,你需要的是在后端写点脚本就可以完成这个任务了。利用下面的几个DWF提供的调用即可实现目标:”
启动Python脚本并传参
this.sh.execute
这个后端脚本的函数,可以直接启动python脚本,参数为一个字符串,将完整的python脚本路径输入后即可启动执行,例如,假设服务器上有一个名为analytic.py的脚本在/home/scripts路径下,那么调用可以这么写:
...
代码块 | ||
---|---|---|
| ||
import sys ... def main(): if len(sys.argv) > 1 : oid = sys.argv[1] ... |
如果希望在python脚本里简单的输出一些数据给dwf的后端javascript脚本,可以使用下面的写法:
代码块 | ||
---|---|---|
| ||
let scriptPath = "/home/script/analytic.py";
let oid = this.obj.oid;
let r = this.sh.execute(`python ${scriptPath} ${oid}`); |
而在对应的python脚本里通过print函数可以将结果返回即可:
代码块 | ||
---|---|---|
| ||
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脚本的时候作为启动参数传递进去即可。
...
代码块 | ||
---|---|---|
| ||
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即可将结果文件附加到指定的属性上。
...
代码块 | ||
---|---|---|
| ||
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之中。
尾声
说话之间,已经到了傍晚,只见万道霞光照进办公室,小伍看了看窗外,感觉信心十足,答到:“哦,原来是这样的,好!我这就回去试试!”。说话之间,已经到了傍晚,只见霞光照进办公室,小伍看了看窗外,感觉信心十足,答到:“哦,原来是这样的,好!我这就回去试试!”。
“别急!”,我说:“先开完组会再说,马上就要开始了。”,只听熟悉的声音响起,全组开始汇报大家开始汇报工作,晚上继续工作。
...