EbsScript是EBSILON专业版中的一种嵌入式脚本语言,它使你能够使用EBSILON的输入、输出和计算功能,并将这些功能与您自己的代码相结合:
EbsScript可以在以下6种情况下使用
本入门指南旨在为那些熟悉其它编程语言和一般编程的人提供一个EbsScript的快速参考。它提供了EbsScript语法的概述,常见任务的代码片段示例和调试技巧。更多关于使用EbsScript的细节可以在EBSILON帮助系统的EbsScript章节中找到。EbsScript工具的三个关键部分,(编辑器(第10.14节)、资源管理器和执行器)也在该章中描述。
EbsScript 语法 EbsScript 操作符 EbsScript 决策语句 EbsScript 循环语句 EbsScript 函数
EbsScript 变量范围 EbsScript 数组 EbsScript 调试 EbsScript 接口单元 EbsScript 示例EbsScript的语法是基于PASCAL编程语言的语法加上一些扩展。更确切地说,这意味着:
const PI = 3.14159;
{ This is a single line comment in EbsScript}
// This is also a single line comment in EbsScript
在EbsScript编辑器中,注释文本以灰色显示。
var
val1, val2 :real;
offset : integer;
name : string;
flag : boolean;
pipe : ebsPipe ;
cmp : ebsComp ;
除了以上高层规则外,以下具体内容也很有用。
EbsScript中的语句使用了特定的PASCAL词(或 "Delphi "属性),这些词被称为保留词或关键字。例如,program for、while、do、var、real、begin和end这些词都是保留词。您不能使用保留词作为变量和函数/过程的名称。下表显示了EbsScript中的保留词/关键字:
A |
||||
abstract |
add |
addr |
and |
append |
array |
autodispatch |
autosafearray |
autounknown |
|
B |
||||
begin |
bitand |
bitnot |
bitor |
bitwiseand |
bitwiseor |
bitwisexor |
bitxor |
boolean |
break |
C |
||||
callback |
capacity |
case |
char |
class |
comnullptr |
const |
constructor |
continue |
copy |
copymem |
||||
D |
||||
dec |
default |
destructor |
dispatchcall |
dispose |
div |
divide |
do |
downto |
|
E |
||||
else |
end |
enumstrings |
enumtostring |
equal |
except |
explicit |
|||
F |
||||
false |
finalization |
finally |
for |
forward |
freemem |
function |
|||
G |
||||
getmem |
greaterthan |
greaterthan |
||
H |
||||
high |
||||
I |
||||
if |
implementation |
implicit |
in |
inc |
index |
inherited |
initialization |
insert |
internal |
intdivide |
integer |
interface |
||
L |
||||
leftshift |
length |
lessthan |
lessthanorequal |
logicaland |
logicalnot |
logicalor |
logicalxor |
low |
|
M |
||||
mod |
modulus |
multiply |
||
N |
||||
negative |
new |
nil |
not |
notequal |
O |
||||
of |
on |
operator |
or |
otherwise |
overload |
override |
|||
P |
||||
pointer |
positive |
|
println |
private |
procedure |
program |
property |
protected |
public |
R |
||||
raise |
read |
real |
record |
reintroduce |
remove |
repeat |
reserve |
return |
rightshift |
round |
||||
S |
||||
self |
setlength |
setlow |
shl |
shr |
sizeof |
sliceleft |
slicemid |
sliceright |
smartptr |
static |
step |
string |
subtract |
|
T |
||||
then |
to |
true |
trunc |
try |
type |
typeof |
|||
U |
||||
unit |
unittest |
until |
uses |
|
V |
||||
var |
virtual |
|||
W |
||||
while |
write |
|||
在EbsScript编辑器中,EbsScript的保留词将以蓝色文本显示。
程序中的 "标识符 "指的是一个对象(如数据类型、变量或函数),它的名称是唯一的。因此,标识符必须在一个命名空间中是唯一的。
通常,标识符使用一串字母和数字的组合。
操作符 |
描述 |
+ |
两个操作数相加 |
- |
从第一个操作数中减去第二个操作数 |
* |
两个操作数相乘 |
div |
整数相除 |
mod |
模数运算符和整数除法后的余数 |
/ |
浮点(实数)除法 |
Exponentiation |
使用指数函数pow pow(base, exponent) |
操作符 |
描述 |
= |
检查两个操作数的值是否相等,如果是,则条件为真。 |
<> |
检查两个操作数的值是否相等,如果值不相等,则条件为真。 |
> |
检查左操作数的值是否大于右操作数的值,如果是,则条件为真。 |
< |
检查左操作数的值是否小于右操作数的值,如果是,则条件为真。 |
>= |
检查左操作数的值是否大于或等于右操作数的值,如果是,则条件为真。 |
<= |
检查左操作数的值是否小于或等于右操作数的值,如果是,则条件为真。 |
操作符 |
描述 |
and |
布尔 AND 操作符。如果两个操作数都为真,则条件为真。 |
and then |
与AND运算符类似,但是,它保证了编译器对逻辑表达式的评价顺序。从左到右和右边的操作数只有在必要时才会被评估。 |
or |
布尔OR运算符。如果两个操作数中的任何一个为真,则条件变为真。 |
or else |
与布尔OR类似,但是,它保证了编译器对逻辑表达式的评价顺序。从左到右和右边的操作数只有在必要时才会被评估。 |
not |
布尔NOT运算符。用于反转操作数的逻辑状态。如果一个条件为真,那么NOT运算符将使其为假。 |
如果布尔表达式条件值评估为真,那么 if 语句内的代码块将被执行。如果布尔表达式的值评估为假,那么将执行if语句结束后的第一组代码(end; 结束之后;)。
if str <> "" then begin
str := StringTrimLeft(str);
str := stringLower(str) ;
end;
EbsScript认为任何非零或非零的值都为真。零或零的值被视为假。
如果布尔表达式条件评价为真,那么将执行 if-then 代码块,否则将执行 else 代码块。例如:
if str <> "" then begin
str := StringTrimLeft(str);
str := stringLower(str) ;
end
else begin
UserInput := "" ;
end;
Case 分支语句的语法是:
case (expression) of
L1: S1;
L2: S2;
...
Ln: Sn;
otherwise: Sotherwise; or else: Selse
end;
其中,L1, L2... Ln 是 case 标签或输入值,可以是整数、字符、布尔值或枚举数据项。S1,S2,... Sn是 EbsScript 语句,每个语句都可以有一个或多个 case 标签与之关联。这个表达式被称为 case 选择器或 case 索引。case 索引可以假设与 case 标签对应的值。case 语句必须始终有一个与之相关联的结束语句。下面的规则适用于 case 语句:
例如:
case MacroInterface.LoadMethod of
0 : Programmable.SPEC3 := 0 ;
1 : Programmable.SPEC3 := 2 ;
2 : Programmable.SPEC3 := 1 ;
otherwise Programmable.SPEC3 := MacroInterface.LoadMethod ;
end;
EbsScript 中的 while-do 循环语句允许重复计算,直到满足某些测试条件。换句话说,只要给定的条件为真,它就会重复执行一个目标语句或一组语句。while-do 循环的语法是:
while (condition) do S;
其中条件是一个布尔或关系表达式,其值为真或为假,S 是 "begin ... end;" 板块中的一条或一组语句。例如:
while number > 0 do
begin
sum := sum + number;
number := number - 1;
end;
当条件变成假时,程序控制权就会传递到紧跟在循环 end; 语句后面的那一行。
"for ... do" 循环是一种重复控制结构,它允许你有效地编写一个需要执行特定次数的循环。EbsScript 中 for-do 循环的语法如下:
for < loop-variable-name > := < initial_value > to [downto] < final_value > do S;
其中,loop-variable-name 指定了一个序数类型的变量,称为控制变量或索引变量; initial_value 和 final_value 是控制变量可以取的值;S 是 for-do 循环的主体,可以是一条语句或一组语句。例如:
for i:=1 to n do
begin
MacroInterface.ERRARRAY.x[i] := Programmable.RA_2.x[i];
MacroInterface.ERRARRAY.y[i] := Programmable.RA_2.y[i];
end;
这是 for-do 中为-做循环的控制流程:
"repeat ... until" 循环与 while 循环类似,不同的是 "repeat ... until" 循环至少保证执行一次。EbsScript中 "repeat ... until" 循环的语法如下:
Repeat
S1;
S2;
...
Sn;
until condition;
For example:
repeat
sum := sum + number;
number := number - 1;
until number = 0;
请注意,条件表达式出现在循环的最后,所以在循环条件被测试之前,循环中的语句将被执行一次。如果条件为真,控制流会跳回重复,循环中的语句会再次执行。这个过程重复进行,直到给定的条件变成假。
EbsScript 中的 break 语句有以下用途:
EbsScript中的 continue 语句与 break 语句的工作原理类似。然而,continue 不是强制终止,而是强制循环的下一次迭代,跳过中间的任何代码。当在 "for ... do" 循环中使用时,continue 语句会使循环中的条件测试和增量部分执行。当在 "while ... do" 和 "repeat...until" 循环中使用时,继续语句会使程序控制传递给循环的条件测试。
EbsScript 中的函数定义由函数头、本地声明和函数体组成。函数头由关键字函数和一个函数名称组成。这些是函数声明的部分:
Arguments 参数: 参数建立了调用程序和函数标识符之间的联系,也称为形式参数。
Return Type 返回类型: 所有的函数都必须返回一个值,所以所有的函数都必须分配一个类型。函数类型是函数返回值的数据类型。它可以是标准的、用户定义的标量或子范围类型,但不能是结构类型。
Local declarations 本地声明: 本地声明是指标签、常量、变量、函数和程序的声明,它只适用于函数主体。
Function Body 函数体: 函数体包含一系列定义函数作用的语句。它应该总是用保留字 begin 和 end 包围起来。函数体是函数中完成所有计算的部分。在函数体中必须有一个类型为 function_name := expression 的赋值语句来为函数名赋值。这个值会在函数执行时返回。函数体中的最后一条语句必须是 end 语句。调用函数时,只需将所需参数与函数名一起传递,如果函数返回一个值,则可以将返回的值赋值给一个变量来存储。例如:
function test (a, b: Integer ; c: real) : real;
begin
test:= (a+b)*c;
end;
begin
println ( test(2, 5, 7.1));
end.
也请注意最后函数 end 语句后的分号。
在任何编程中,范围是指程序中定义的变量存在的那个区域或部分。超出这个范围或在其范围之外,就不能访问该变量。在 EbsScript 中有三个地方可以声明变量:
本地变量
在函数/过程或代码块中声明的变量称为本地变量。它们只能被该函数/进程或代码版块中的语句使用。本地变量不为自己以外的函数/过程或代码版块所知。
全局变量
全局变量是在函数之外定义的,通常在脚本的顶部。全局变量将在脚本的整个生命周期中保持其值,并且它们可以在脚本中定义的任何函数中被访问。全局变量可以被任何函数访问。也就是说,一个全局变量在声明之后就可以在整个脚本中使用。
参见 Free Pascal language reference 中关于变量范围的更多通用信息。
Ebscript 提供了一种叫做数组的数据结构,它可以存储一个固定大小的同类型元素的顺序集合。数组用于存储相同类型的变量集合。我们不需要声明单个变量,如 number1, number2, ... number100,而是声明一个名为 numbers 的数组变量,并使用数字 numbers[1], numbers[2], and ..., numbers[100] 来表示单个变量。数组中的一个特定元素是通过索引来访问的。所有数组都由连续的内存位置组成。最低地址对应第一个元素,最高地址对应最后一个元素。请注意,如果想要一个 C 风格的数组从索引 0 开始,只需要从 0 开始索引,而不是 1。
一维数组的类型声明的通用形式是:
type array-identifier = array[index-type] of element-type;
其中:
array-identifier 表示数组类型的名称。
index-type 是数组的下标,可以是除实数以外的任何标量数据类型。
element-type 指定要存储的值的类型。
例如:
type
vector = array [ 1..25] of real;
var
velocity: vector;
velocity 是一个 vector 的变量数组,它足以容纳25个实数。如果要从 0 开始索引数组,声明如下::
type
vector = array [ 0..24] of real;
var
velocity: vector;
数组下标的类型
在EbsScript中,数组下标可以是任何标量类型,比如整数、布尔、枚举或子范围,实数除外。数组下标也可以有负值。例如:
type
temperature = array [-10 .. 50] of real;
var
day_temp, night_temp: temperature;
最后是一个下标是字符类型的例子:
type
ch_array = array[char] of 1..26;
var
alphabet: ch_array;
c: char;
begin
...
for c:= 'A' to 'Z' do
alphabet[c] :=
元素通过数组名称的索引被访问。这是通过将元素的索引放在数组名称后的方括号内来实现的。例如:
a: integer;
a: = alphabet['A'];
a:= numbers[10];
uses @KernelScripting
由于 Kernel Scripting 内核脚本是嵌入到整个方程系统的求解器中的,你不能在编辑器中调试它。如果你编辑一个 Kernel Scripting 内核脚本,EbsScript 编辑器中的所有调试功能都将被禁用。为了绕过这个限制,你可以用打印 println() 指令打印中间结果,比如:
println("FCTRLMODE =", ::Control.FCTRLMODE);
并在计算后在 EbsKern-Ouput 输出工具栏中查看它们。要显示 EbsKern-Ouput 输出工具栏,请按照以下步骤进行:
1. 从 View 查看下拉菜单中选择 Toolbars 工具栏。
2. 从子菜单中选择 EbsKern-Ouput 输出,显示如下图所示的 EbsKern-Ouput 输出窗口:
在 EbsKern-Ouput 输出栏中,为正在运行的模型中包含的每个组件93创建一个表(tab),同时为所有的输出创建一个完整的表(tab)。
如果你的脚本代码没有使用 Kernel Scripting,那么你可以在 EbsScript 运行时调试你的代码。调试可以中断脚本的执行,查看和改变变量的值。EbsScript 调试器可以在 Debug 菜单项下找到。
为了调试,必须在脚本中设置断点,方法是将光标移动到 EbsScript 编辑器窗口中所需要的行然后点击 F9("切换断点")。如果再点击一次 F9 将会删除断点。
然后可以用 F5(开始/继续)启动脚本并且运行到设置的第一个断点。可以看到监控栏中显示的变量值(在EbsScript编辑器中的查看 > 工具栏 > 控制台(View > Toolbars > Console)),并可以更改它们。
然后可以分别用 F10 (跨进程激活)和 F11(进入过程)继续一步步地执行脚本,或者用 F5 运行到下一个断点。调试器窗口如下图所示:
注意,你也可以通过复制你的内核脚本的内容到一个外部脚本来运行调试器,只要它不是通过使用 "ksGetPipeValue "等函数来访问流或组件数据的。为了正确地解释模型的变量名,外部脚本必须在相同的上下文中运行(即在模型级别上默认情况下称为(全局),或者在包含内核脚本的相应宏对象中,在上面的例子中称为 "控制")。可以从 EbsScript 菜单中的下拉菜单中选择合适的上下文,如上图中红色圆圈所示。
如果你的内核脚本确实访问了流或组件数据,那么使用 "println "语句和 EbsKern-Output 输出窗口是目前唯一可用的调试手段。
uses @KernelScripting;
单元名称前的"@"是接口单元的标识。EBSILON Professional提供以下接口单元:
KernalScripting
Fluid
Variant
Units
Covariance
System
Epos
KernelExpression
MacroHelpers
TimeSeries
NNTool
Standard
Debug
可以通过打开 EbsScript Editor 编辑器(选择计算下拉菜单中的 EbsScript Editor...项或按 F8 键),然后选择 EbsScript Editor 编辑器的 EbsScript 下拉菜单中的 Open Interface Unit > 项来查看这些接口单元提供的功能。注意,有一些接口单元,例如 NNTool 可能需要额外的授权。
在Pascal中也可以定义自定义单元,这些单元可以被任何脚本使用。
下面是一个自定义单元的例子:
unit EngineCalcs;
interface
procedure EnginePreProcessing;
procedure EnginePostProcessing;
procedure DoMyCalculations( par1:real; var retval1:real, var retval2:real);
implementation
uses @kernelscripting,@fluid;
procedure AddMessage( MessageNumber:integer; MessageMessage: string);
var len:integer;
begin
//::StatusText.text:=printToString( ::StatusText.text, "\n",MessageMessage );
len:=MacroInterface.Messages.getSize+1;
MacroInterface.Messages.size:=len;
MacroInterface.Messages.x[ len ]:= MessageNumber;
end;
procedure EnginePreProcessing;
var i,j:integer;
x,y:real;
begin
…
…
…
end;
procedure EnginePostProcessing;
var i,j:integer;
x,y:real;
begin
…
…
…
end;
procedure DoMyCalculations( par1:real; var retval1:real, var retval2:real);
var i,j:integer;
x,y:real;
begin
…
…
…
retval1 := …;
retval2 := …;
end;
end.
使用实例:
来自 "计算前的脚本 "中的一个宏:
uses EngineCalcs;
begin
EnginePreProcessing;
end;
来自组件 93 或 "内核表达式"(=组件中的特殊输入字框):
uses EngineCalcs;
var p1, r1,r2;
begin
p1:= sqrt(23);
DoMyCalculations(p1,t1,r2);
…
…
end;
对模型中的不同工况(Profile)进行模拟运行:
var
i: integer;
begin
for i:=1 to 10 do
begin
setCalcProfileByName(“first_profile_name”);
simulate ;
setCalcProfileByName(“second_profile_name”);
simulate ;
end;
end;
测量单位换算 -- 压力换算成psia
可以在 EbsScript 中直接访问模型中的数。请注意,模型中的数在内部使用 SI 单位制来存储。有单位的模型数的测量单位(UOM)如下:
因此,像这样的直接赋值
Turbine.M1N := 100;
始终表示 100 kg/s。EbsScript 提供的 Units 接口单元允许在脚本代码中进行测量单位转换。下面的例子是将压力从 bar 转换为 psia:
uses @Units
unitsConvert(comp122.P1, “BAR”, myPinPsia, “PSIA”) ;
下面是 Ebsilon 中所有可用的测量单位列表的链接:
Ebsilon Units Of Measure
用 EbsScript 设置设定规范值要用 EbsScript 设置设定规范值,请将计算所需设定规范值的函数的 EbsScript 代码粘贴到给定输入栏中。例如:
function evalexpr:REAL;
var
begin
// TODO: calculate specification value
evalexpr:= calc_result
end;
进行属性计算
可以在 EbsScript 中调用任何 EBSILON 属性来进行计算。例如,从压力和温度计算焓值:
uses @fluid;
var
Pres,Temp,rResult:real;
iphase:PhaseEnum;
begin
Pres:=1.013;
Temp:=20.0;
fluidTableWST( rResult, iPhase, Pres, Temp, FuncH_OF_PT);
println (rResult);
end;
下面这部分代码可以让用户在与流类型相关的情况下更加方便:
begin
fluidTableObject( {var rResult:Real}, {var phase:PhaseEnum}, {arg1:Real}, {arg2:Real}, {func:FuncEnum}, {obj:ebsData} );
end;
最后一个参数 (obj:ebsData) 代表一个流,它可以是流表中的名称,也可以是一个端口的参照 (例如:'componentname._2'代表连接到端口 2 的流)。
下面是 EbsScript 关键词窗口中接口单元--流体(Interface Unit – Fluids)的可用函数截图:
在下面的链接下是可用的属性函数调用列表,它可以从 EbsScript 中来访问:
从组件上的端口获取 P、H 和 M
uses @kernelscripting;
var
m14,h14,p14:real;
begin
m14:=ksGetPipeValue(14,PhysValueM);
h14:=ksGetPipeValue(14,PhysValueH);
p14:=ksGetPipeValue(14,PhysValueP);
end;
检查宏对象上的某一个端口(端口7)是否连接,并获取上游组件:
uses @MacroHelpers
var
txt : string;
ThisMacro : ebsMacro ;
pipe : ebsPipe ;
cmp : ebsComp ;
ThisMacro := getContainerMacro(MacroInterface); // Check if logic line is connected
if isNull(getPipeAtLink(ThisMacro,7)) then
begin
txt := ThisMacro.name + " fuel logic line at Pin 7 not connected." ;
MessageBox(txt)
exit(-1, txt); // terminate the run and set error message to txt
end;
// There's a logic line at Pin 7. Get it.
pipe := getPipeAtLink(ThisMacro,7) ; // Check if it's connected to a pipe
if isNull(GetPipeAtLogpipe(pipe, 1)) then
begin
txt := ThisMacro.name + " logic line at Pin 7 not connected to a pipe." ;
MessageBox(txt);
exit(-1, txt); // terminate the run
end;
// It's connected to a pipe. Get it.
pipe := GetPipeAtLogpipe(pipe, 1) ;
// Get component at upstream end of that pipe
cmp := GetCompAtPipe(pipe, 1) ;
COM 对象中的调用代码
callEbsScriptServer( {class:string}, {var var1:(array of) primitive type}, {var var2:(array of) primitive type}{, callBackProc:callback procedure = nil} );
参见示例 Block 750.ebs