PCB 规则引擎之脚本语言JavaScript应用评测
世界上没有好做的软件,觉得好做,只是你的系统简单而已,而不是哪个行业简单,特别像我们PCB制造企业务逻辑的很复杂的,仅仅靠决策树中的每个节点布置决策逻辑是不能满足要求的,所以我们在制作PCB规则引擎必须再向更高一层次考虑,让规则管理灵活度更高,控制力度更大的决策逻辑组件。当然一个好的规则引擎对脚本语言的支持是必不可少的,如何选择脚本语言是规则引擎选型非常重要一环,需要考虑,用户对脚本的易学,易用,脚本的性能,脚本语言与.net语言深度交互能力,
写了一个工具专用于对JS进行测试,语法支持,性能,交互性进行测试.
一.基本语法测试
1. 检测流程是否存在
var ppeflow =new Array('开料','钻孔','沉铜','板镀','干膜','图形电镀','退膜','蚀刻','退锡','阻焊','字符','喷锡','铣板','测试','终检'); if (ppeflow.exists ("开料")) { console.alert('存在'); } else { console.alert('不存在'); } /* 判断数组中是否存在 */ Array.prototype.exists = function (val) { for (var i = 0; i < this.length; i++) { if (this[i] == val) { return true; } } return false; }
2 检测流程顺序
//查找制定元素在数组中的索引值 Array.prototype.indexVf=function(arr){ for(var i=0;i<this.length;i++){ if(this[i]==arr){ return i; } } } var ppeflow =new Array('开料','钻孔','沉铜','板镀','干膜','图形电镀','退膜','蚀刻','退锡','阻焊','字符','喷锡','铣板','测试','终检'); var index1 = ppeflow.indexVf('字符'); var index2 = ppeflow.indexVf('喷锡'); if (index1< index2) { console.alert('字符在喷锡前'); } else { console.alert('字符在喷锡后'); }
3 判断孔径比值
var holesize = 0.2; var thick = 2.5; //判断孔径比是否大于12:1 var scale = thick / holesize; if (scale > 12) { console.alert('孔径比值大于12'); } else { console.alert('孔径比值小于或等于12'); }
4 判断客户代码等于T001
var pdctno = 'at00101ca0'; //客户代码在生产型号中间2到5位 pdctno = pdctno.toUpperCase(); //转为大写 var custcode = pdctno.substr(1,4); if (custcode == 'T001') { console.alert('客户代码等于T001'); } else { console.alert('客户代码不等于T001'); }
5 求钻孔表总孔数
var drlTable = [ {'HoleType':'PTH','HoleSize':3.175,'HoleCount':5,'DisplayOrd':0}, {'HoleType':'PTH','HoleSize':0.2,'HoleCount':15,'DisplayOrd':1}, {'HoleType':'PTH','HoleSize':0.3,'HoleCount':52,'DisplayOrd':2}, {'HoleType':'PTH','HoleSize':1.0,'HoleCount':44,'DisplayOrd':3}, {'HoleType':'PTH','HoleSize':2.0,'HoleCount':11,'DisplayOrd':4}, {'HoleType':'PTH','HoleSize':3.5,'HoleCount':20,'DisplayOrd':5}, {'HoleType':'PTH','HoleSize':4.0,'HoleCount':18,'DisplayOrd':6} ]; var HoleCount = 0; for(var i in drlTable) { HoleCount += drlTable[i].HoleCount ; } console.alert('总孔数:' + HoleCount) ;
6 板厚喷锡板尺寸判断
/* 判断数组中是否存在 */ Array.prototype.exists = function (val) { for (var i = 0; i < this.length; i++) { if (this[i] == val) { return true; } } return false; } var thick = 0.4; var surface = '喷锡'; var pnlwidth = 18; var pnlheight = 24; //判断当板厚<=0.5mm,表面处理为喷锡时,PNL尺寸不能大于16X18 if (thick < 0.5 && ['喷锡','有铅喷锡','无铅喷锡'].exists(surface) ) { if (pnlwidth > 16 || pnlheight > 18) { console.alert('板厚<=0.5mm,表面处理为喷锡时,PNL尺寸不能大于16X18'); } } else { console.alert('PNL尺寸符合要求'); }
小结:通过实际PCB工程基本检测规则对JS语法验证,选用JS语言作为PCB工程客户端进行规则维护还不错哦!JS语法友好,易学,易用。
二.性能测试
1.JS循环1亿次 耗时839毫秒
var sum = 0; for (var i=0; i<100000000; i++) { sum ++; }
2.C#外循环1000次,JS内循环10W次 合计1亿次 耗时3222毫秒
var sum = 0; for (var i=0; i<100000; i++) { sum ++; }
3.JS调函数循环1亿次 耗时8416毫秒
var sum = 0; for (var i=0; i<100000000; i++) { var rndNum = Math.ceil(Math.random() * 10); sum += sumsxi(rndNum,i); } function sumsxi(a,b) { return a + b; }
4.C#外循环1000次, JS调用函数内循环10W次 合计1亿次 耗时17305毫秒
var sum = 0; for (var i=0; i<100000; i++) { var rndNum = Math.ceil(Math.random() * 10); sum += sumsxi(rndNum,i); } function sumsxi(a,b) { return a + b; }
小结:通过以上测试:脚本语言能达到这个速度水平非常棒了,我们来看看这个速度是否能满足实际要求呢,
通常执行一次JS V8引擎实例化一次就好了(即在C#中执行),第一次将,列表数据,对象数据,C#函数,JS函数装载进去后,接着规则引擎主要耗时将在执行脚本上面了,比如:一个规则数结构最多按100个节点,应该足够了,然后每个节点存在JS可执行代码1000行,考虑到实际更复杂,将1000行代码复杂度乘以10倍,那么这里按1W行的代码量计算;100个节点乘以1W行代码量,就是100W行代码量,实测耗时:100毫秒。这个速度顶呱呱的.
但实际应用中还待验证(比如多用并发,更复杂的业务逻辑超耗时,大量数据(10M数据量)进入JS规则引擎参与到数据引用与计算).
三.交互性测试(JS内置对象返回)
把业务逻辑用JS脚本写,并用JS V8引擎执行脚本, 并将计算结果返回给C#
测试:JS脚本中的内置对象,如:单个值,对象,数组 类型,进行业务逻辑处理后并将对象变量返回给C#
1.JS内置单值
例1:计算孔径比
//计算孔径比值 var holesize = 0.2; var thick = 2.5; var scale = thick / holesize; scale;
例2:通过表面处理ID号找到对应的表面处理名称
var arr =[ ['喷锡','A01'],['沉金','A02'],['OSP','A03'],['镀金','A04'],['沉银','A05'],['沉锡','A06']] ; var SurfaceCode = 'A03'; //表面处理ID号 var SurfaceName = ''; //通过ID号找到对应的表面处理名称 for(var i in arr) { if (techno == arr[i][1]) { SurfaceName = arr[i][0]; } } console.log(SurfaceName) SurfaceName
2.JS内置对象
JS对象返回给C#类型转为:字典Dictionary<string,Object>类型
例3:将JS中的钻孔对象返回给C#
var HoleMod = { 'HoleType':'PTH','HoleSize':0.2,'DisplayOrd':1}; HoleMod.HoleCount = 22; HoleMod
3.JS内置数组
JS 数组返回给C#类型转为:Object[]类型
例4:将JS中的钻孔表数组返回给C#
var drlTable = [ {'HoleType':'PTH','HoleSize':3.175,'HoleCount':5,'DisplayOrd':0}, {'HoleType':'PTH','HoleSize':0.2,'HoleCount':15,'DisplayOrd':1}, {'HoleType':'PTH','HoleSize':0.3,'HoleCount':52,'DisplayOrd':2}, {'HoleType':'PTH','HoleSize':1.0,'HoleCount':44,'DisplayOrd':3}, {'HoleType':'PTH','HoleSize':2.0,'HoleCount':11,'DisplayOrd':4}, {'HoleType':'PTH','HoleSize':3.5,'HoleCount':20,'DisplayOrd':5}, {'HoleType':'PTH','HoleSize':4.0,'HoleCount':18,'DisplayOrd':6} ]; drlTable;
例5:将JS中的表面处理数组返回给C#
var arr = ['OSP','沉金','喷锡']; arr[6] = '沉锡'; arr;
4.JS内置函数
例6:在JS中写函数并在JS中调用应, 此函数不能返回给C#
//计算孔径比JS函数 function HoleSizeThickSclae(HoleSize,Thick) { return Thick/HoleSize; } var holesize = 0.2; var thick = 2.5; HoleSizeThickSclae(holesize,thick);
四.交互性测试(C#传参与返回)
把业务逻辑用JS脚本写,用JS V8引擎执行脚本, 并将计算结果返回给C#
测试:将C#中的对象变量传给JS脚本,如:单个值,对象,数组,字典 等类型,JS脚本接受此类型数据后进行业务逻辑处理,处理完成后,再将对象变量返回给C#.
1.C#传入单值
C#传入数据:
string str1 = "样板"; int num1 = 2018;
JS接受数据并处理,返回单个值
//原始C# 单个值输出 console.log(num1); console.log(str1); console.log('================='); //更改C# 单个值 num1 += 10 str1 = '钻孔' + num1.toString(); //更改后C#对象值输出 console.log(num1); console.log(str1); console.log('================='); //返回给C# str1;
2.C#传入对象
C#传入数据:
ppeflow ppeflow = new ppeflow (); ppeflow.Num = 1; ppeflow.TechName = "开料";
JS接受数据并处理,返回对象
//原始C#对象输出 console.log(ppeflow.Num); console.log(ppeflow.TechName); //更改C#对象值 ppeflow.Num = 2 ppeflow.TechName = '钻孔'; //更改后C#对象值输出 console.log(ppeflow.Num); console.log(ppeflow.TechName); //返回给C# ppeflow;
3.C#传入数组
C#传入数据:
List<ppeflow> ppeflowList = new List<ppeflow>() { new ppeflow(){ Num = 1, TechName = "开料"}, new ppeflow(){ Num = 2, TechName = "钻孔"}, new ppeflow(){ Num = 3, TechName = "沉铜"}, new ppeflow(){ Num = 3, TechName = "板镀"}, new ppeflow(){ Num = 3, TechName = "干膜"}, new ppeflow(){ Num = 3, TechName = "图镀"}, new ppeflow(){ Num = 3, TechName = "退膜"}, new ppeflow(){ Num = 3, TechName = "蚀刻"}, };
JS接受数据并处理,返回数组
ppeflowList[0].TechName = 'ECN'; //更改0号数组值 ppeflowList[11] = { "Num":9,"TechName":"FQC"} //通过JS增加对象 给到11号数组 ppeflowList[12] = ppeflowList[1] //将C# 1号数组 赋值给12号数组 ppeflowList[12] .TechName = '包装' //修改12号数组值 for (var i in ppeflowList) { console.log(ppeflowList[i].TechName) //查看数组内容 } ppeflowList; //返回给C#数据
JS处理后的数组返回给C#的数据变化关系
4.C#传入字典
C#传入数据:
Dictionary<string, string> dic = new Dictionary<string, string>(); dic.Add("01", "开料"); dic.Add("02", "钻孔"); dic.Add("03", "干膜");
JS接受数据并处理,返回字典
dic['01'] = '流程指示'; //修改C#字典值 dic['04'] = 'FQC'; //增加字典值 dic; //返回给C#
5.C#传入函数
C#传入数据:
calc calc = new calc(); public class calc { public int sum(int a,int b) { return a + b; } }
JS调用C#函数,并返回结果
var aa = 18; var bb = 2000; var cc = calc.sum(aa,bb); //调用C#函数加算计算 console.log(cc); cc;
五.JS扩展
1.扩展函数console 给JS使用
C#写函数部分
public class console { public event EventHandler logHandler; /// <summary> /// 打印log /// </summary> /// <param name="msg"></param> public void log(object msg) { if (logHandler != null) logHandler(msg, null); } /// <summary> /// 打印到控制台 /// </summary> /// <param name="msg"></param> public void print(string msg) { Console.WriteLine(msg); } /// <summary> /// 弹窗提示 /// </summary> /// <param name="msg"></param> public void alert(string msg) { MessageBox.Show(msg, "JavaScript提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } /// <summary> /// 输入窗 /// </summary> /// <param name="msg">输入框信息</param> /// <param name="defVal">默认值</param> /// <returns></returns> public string prompt(string msg, string defVal = "") { return Interaction.InputBox(msg, "JavaScript输入", defVal); } /// <summary> /// 确认Yes或No窗口 /// </summary> /// <param name="msg">提示框信息</param> /// <returns></returns> public bool confirm(string msg) { DialogResult result = MessageBox.Show(msg, "JavaScript确认", MessageBoxButtons.YesNo, MessageBoxIcon.Information); return result == DialogResult.Yes; } }
JS调用C#函数
for (var i=0;i<10;i++){ console.log('log输出' +i); } var cc = 'pcbren 致力于PCB工程自动化研究'; console.alert(cc); var tt = console.prompt('请输入:pcbren ','test'); while (tt !='pcbren')// 输入pcbren 否则死循环 { tt = console.prompt('请输入:pcbren ','test'); }
JS调用C#函数测试界面
2.扩展函数JSON给JS使用
C#写函数部分
public class JSON { /// <summary> /// 对象转Json字符串 /// </summary> /// <param name="Object"></param> /// <returns></returns> public string stringify(object Object) { return Newtonsoft.Json.JsonConvert.SerializeObject(Object); } /// <summary> /// Json字符串转对象 /// </summary> /// <param name="StirngJSON"></param> /// <returns></returns> public object parse(string StirngJSON) { return Newtonsoft.Json.JsonConvert.DeserializeObject(StirngJSON); } }
JS调用C#函数
var holeMod = {'HoleType':'PTH','HoleSize':3.175,'HoleCount':5,'DisplayOrd':0}; var StrJSON = JSON.stringify(holeMod) ;//转换过程: JS对象==>C#字典==》C#字符串==》JS字符串 console.log(StrJSON) ; var Mod = JSON.parse(StrJSON);//转换过程:JS字符串==》C#字符串==>C# JObject对象 console.log(Mod.ToString()) ; Mod;
JS调用C#函数测试界面
3.json数据交换与转换
我们将C#对象数据传递给JS V8引擎,JS脚本可以直接调用C#对象中的属性,如果从C#传入对象Json文本如何处理呢
3.1 C#Json字符串传入JS V8后通过JS内置eval 方法转为对象
C#代码部份
string strJSON = "{'Num':1,'TechName':'开料'}";
JS转对象部份
console.log(strJSON); //JSON传入JSON字符串 var obj = eval('(' + strJSON + ')'); //将JSON字符串转为JS对象 console.log(obj.Num); console.log(obj.TechName);
3.2 C#Json字符串内嵌JS V8 变量为对象如 var = {'加投率:' 1.05};
JS代码部份
var objinput = {'Num':1,'TechName':'开料'}; //这代码码由C#在执行JS 之前内嵌在其中, //对于用户看不见此行代码,但可以调用此对象 for(var i in objinput) { console.log(objinput[i]); }
作者:pcbren 微信号:yadnfku QQ号: 254566449
博客地址:https://www.cnblogs.com/pcbren/
声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权,贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。
如果大家感觉我的博文对大家有帮助,请推荐支持一把。