资源描述
第三十章宏指令说明.230.1 宏指令的结构.230.2 宏指令的语法.430.2.1 常量和变量.430.2.2 运算符.730.3 语句.10303.1定 义语句.1030.3.2 赋值语句.1130.3.3 逻辑运算语句.1130.3.4 循环语句.13303.4其 他控制命令.1530.4 子函数.1630.5 内置函数功能.1730.5.1 数学运算函数.1830.5.2 数据转换函数.2430.5.3 数据操作函数.2930.5.4 位状态转换.3430.5.5 通讯有关的函数.3730.6 怎样建立和执行宏指令.5230.6.1 怎样建立一个宏指令.5230.6.2 执行宏指令.5730.7 使用宏指令时的注意事项.5830.8 使用自由协议去控制一个设备.5930.9 编译错误提示信息.6730.10 宏指令范例程序.781第三十章宏指令说明宏指令提供了应用程序之外附加的你所需要的功能。在MT8000人机界面运行时,宏指令可 以自动的执行这些命令。它可以担负执行譬如复杂的运算、字符串处理,和使用者与工程之 间的交流等功能。本章主要介绍宏指令的语法、如何使用和编程方法等功能。希望通过本章 的说明,能够使各位能够快速的掌握EB8000软件提供的强大的宏指令功能。30.1宏指令的结构宏指令是由各种语句组成的。这些语句包含常数、变量和各种运算符。这些语句放置在特定 的顺序位置以执行后达到一个希望的执行结果。宏指令的结构一般为以下格式:全局变量声明-可选Sub Function Block Declarations(子函数声明)-可选局部变量声明End Sub(结束子函数)macro_command main()主函数-必须局部变量声明各式语句2pnd mjirrn rcmm分nd结束主函数 _必绅一个宏指令必须有一个且只有一个主函数,用来开始宏指令的执行。格式为:macro_command 函数名称()end macro_command变量声明必须放在宏指令语句的前面,否则如果语句放置在变量声明的前面,将会造成宏指 令无法编译通过。局部变量一般用在宏指令主函数或者自定义的子函数中。它的合法性只在指定的函数中有 效。全局变量一般是定义在所有宏指令函数的前面,且它在整个宏指令中均具有有效性。当局部 变量和全局变量被定义为相同的名称时,只有局部变量有效。下面就是一个简单的宏指令,其中就包含了变量声明和功能执行。macro_command main()short pressure=10SetData(pressure,Allen-Bradley DF1,N7,0,1)end macro_command/局部变量声明/功能执行330.2宏指令的语法30.2.1常量和变量30.2.1.1 常量常量是一个可以被各式语句直接使用的固定的数据。有如下格式:常量类型使用说明举例十进制整型数345,-234,0,23456十六进制数必须以Ox开头0 x3b,Oxffff,0 x237字符型字符必须使用单引号a,data,函数名称布尔型true,false下面即为一个简单的常量使用的范例。macro_command main()short A,B/声明A和B为短整型变量A=1234B=0 xl2/1234 和 0 x12 即为常量end macro_command30.2,1,2 变量 4变量是一个代表着各种资料的名称。在宏指令中,这些资料可以随着宏指令语句执行的结果 改变而改变。变量的命名规则1.必须以英文字母开头2.变量名成长度不超过32个字符3.系统保留寄存器名称不能作为变量名称。下面为5种不同的变量类型:变量类型描述范围Bool布尔型1 bit(一个位)0,1Char字符型8 bits(一个字节)+127Short短整型16 bits(一个字)+32767Int 双整型32 bits(双字)2147418112Float浮点型32 bits(双字)变量声明变量必须在使用前声明。所以,在宏指令,所有的变量都必须在语句使用前都被声明完成。声明变量时,先定义变量的类型,后面再跟着变量名称。如下范例:int ashort b,switchfloatpressure5数组声明宏指令支持一维数组(下标从。开始)。声明数组变量时,先定义数组变量的类型,变量名称,接着就是该数组变量的个数,变量个数必须放置在“”符号中。数组变量的长度为1-4096。一个宏指令中只支持4096个变量。如下范例:int a 10short b 20,switch 30float pressure 15数组的下标最小为0,最大下标为(数组的长度-1)如下范例:char data 100/数组变量的长度是100所以:最小的数组为“data 0”,最大的数组为“data 99”,即100-1=99o变量和数组初始化有两种方法可以让变量初始化:1.使用语句中的赋值语句(二)如下范例:int afloat b 36a=10b 0=12.声明变量时直接赋值char a=5,b=9数组变量的声明是一个特殊的情况。一个完整的数组被初始化时,可以在数组变量声明时,将数据放置在波形括号“”里面,各数据使用逗号分开。如下所示:float data 4=11,22,33,44 这样 data 0=11,data l=22.30.2.2运算符运算符通常被用来指定数据是如何被操作的。在任何一个语句中,运算符左边的变量结果均 依据运算符右边的条件而获得。运算符描述举例二赋值运算符pressure=10数学运算符描述举例+加A=B+C-减A=B-C*乘A=B*C7/除A=B/C%求余(返回剩余数)A=B%5比较运算符描述举例小于if A 10 then B=5=10 then B=5大于if A=大于或者等于if A=10 then B=5=等于if A=10 then B=5不等于if A 10 then B=5逻辑运算符描述举例And与if A 5 then C=10Or或if A=10 or B 5 then C=10Xor异或if A xor 256 then B=5Not非if not A then B=5移位和位运算符通常被用来操作字符型变量、短整型变量和双整型变量的位。在一个语句中,这些运算符的优先权是在从该语句的左边到右边依此执行的。即在语句中左边位置的优先执 行,依次从左到右执行。移位运算符描述举例往左移动指定的位数A=B 8往右移动指定的位数A=B 88位运算符描述举例&位与运算A=B&Oxf1位或运算A=B ICA位异或运算A=B AC位取反运算A二B所有运算符的优先权上述所有运算符的优先权从高到低详细如下所述:位于圆括号里面的运算符最优先 数学运算符 移位和位运算符 比较运算符 逻辑运算符 赋值运算符关键词下面的关键词为宏指令保留使用。这些均不能用来作为变量名称、数组名称或者函数名称等。+,*,/,%,=,=,=,and,or,xor,not,=,&,I,A,exit,macro_command,for,to,down,step,next,return,bool,short,int,char,float,void,if,then,else,break,continue,set,sub,end,while,wend,true,false9SQRT,CUBERT,LOG,LOG 10,SIN,COS,TAN,COT,SEC,CSC,ASIN,ACOS,ATAN,BIN2BCD,BCD2BIN,DEC2ASCII,FLOAT2ASCII,HEX2ASCII,ASCII2DEC,ASCII2FLOAT,ASCII2HEX,FILL,RAND,DELAY,SWAPB,SWAPW,LOBYTE,HIBYTE,LOWORD,HIWORD,GETBIT,SETBITON,SETBITOFF,INVBIT,ADDSUM,XORSUM,CRC,INPORT,OUTPORT,POW,GetError,GetData,GetDataEx,SetData,SetDataEx30.3语句30.3.1 定义语句这个定义语句包含了变量和数组的声明。正式的格式如下:类型 名称 当定义一个名称的类型时举例:int A 定义了变量A为双整型格式类型数组名称数组长度当定义数组名称的类型时举例:intB 10 定义了一维数组变量B的长度为10,类型为双整型1030.3.2 赋值语句赋值语句使用赋值运算符将赋值运算符右边表达式运算的结果放置到运算符左边的变量中。一个运算表达式是由变量、常量和各种运算符组成,执行后产生一个新的数据。变量=表达式举例A=2 这样变量A就被赋值为230.3.3 逻辑运算语句逻辑运算语句是根据逻辑(布尔)表达式的结果来执行相应的动作。它的语句如下所示:单行格式if then Statementselse Statementsend if举例:if a=2 thenb=1else11b=2end if区块格式If then Statementselse if then Statementselse Statementsend if举例:if a=2 thenb=1else if a=3b=2elseb=3end if语法描述:if必须用在该语句的开始部分12必要条件。这是一个控制语句。当Condition为。时,即为FALES”,(条件为假);当Condition为非0时,即为“True(条件为真)。then当Condition执行为“TRUE(真)时,必须放置在需要执行的语句之前。Statements在区块形式中是可选择的参数,在单行形式中,且没有else子句时,为必 要参数,该语句在Condition,为真时执行。else if可选,一条或多条语句,在相对应的Condition-n为true时执行。可选,解释同Conditionelse可选,在上述Condition和Condition-n都不为true时执行。end if必须。在一个if-then语句中使用这个来结束if-then语句。30.3.4循环语句循环语句依据循环条件来反复的执行一个任务。循环语句有两种表达方式。30.3.4.1 for-next 语句For-next语句通常用来执行次数固定的循环任务。一个变量用作为任务执行次数的计数器和 结束循环任务执行的条件。这个变量为固定执行的次数。语法结构如下:for Counter=to step Statementsnext Counter或者13for Counter=down step Statementsnext Counter举例:for a=0 to 10 step 2b=anext a语法描述:for必须用在该语句的开始部分。Counter必要,循环计数器的数值变量,该变量的结果用来计数循环的次数。必要,Counter的初值。to/down必要。用来决定步长是递增还是递减。This determines if the increments or decrements the.“to”递增Counterby.“down”递减Counterby.必要,Counter的终值。测试点。当vConnter大于该值时,宏指令将结束这 个循环任务。step可选,指定StepValue的步长,指定为1以外的数值。StepValue可选,Counter的步长,只能是数值,如果没有指定,则预设为1。Statements可选,for和next之间的语句区块,该语句区块将执行所指定的次数。next必须的。Counter可选。1430.3.4.2 while-wend 语句While-wend语句是用来执行不确定次数的循环任务。设置一个变量用来判断结束循环的条 件。当条件为“True”时,该语句将一直循环执行直到条件变为“False”。语法结构如下:while Statementswend举例:while a 0 thenread_data O=response 4+(response 3 8)/data in 4x_lread_data 1 =response 6+(response 5 8)/data in 4x_2SetData(read_data O,Local HMI,LW,100,2)end ifend macro_command函数名称GetData语法GetData(read_data start,device.函数名称,device_type,address_offset,data_count)or43GetData(read_data,device.函数名称,device_type,address_offset,1)描述获取 PLC 的数据。数据是存储在 read_data startread_data start+data_count-1 这些一维数组变量中。data_count是设定的读取数据的个数。一般来说,read_data是一个一维数组,但是如果data_count是1,read_data可以是一个一维数组,也可以是一个普通 的变量。下面是两种从PLC中读取一个字的方法。ns)CC eiacro_command main()lort read_data_l 2,read_data_2retData(read_data_l O,4FATEK FB Series,RT,5,1).etData(read_data_2,“FATEK FB Series,RT,5,1)nd macro_command此处的device.函数名称,即为在“系统参数”中建立“PLC类蛋的“PLC名称。在此,PLC名称被设定为“FATEKFB Series,:!”时,设定如下图所示。DeviceModel G eneralSecurity FontDevice list:Name L ocationDevice TypeStat.l/FPortIlL uCdl HMI IL ocalMT8xxxN/AN/AN/ARemote HMI ARemote(IP:192,168.0,205,Port.MT8xxxN/AN/AN/ARemote HMI BRemote(IP:210.68.117.224,Po.MTSxxxN/AN/AN/ARemote HMI CRemote aP:210.68.117.224,Po.MTSxxxN/AN/AN/AMITSUBISHI FXOn(L ocal)L ocalMITSUBISHI FX.0RS.COMFATEK(L ocal)L ocalFATEK FB Series1RS.COMdMITSUBISHI FX3uRemote 0P:210.68.117,224,Po.MITSUBISHI FX.0RS.COMFATEK FB Series生 mote(IP:210.68.117.224,Po.FATEK FB Series1RS.COMevice_type是设备类型和PL C中数据的编码方式。例如:如果device_type是44LW_BIN,那么读取的设备类型为LW,数据编码方式为BINo如果使用BIN 编码方式,可以忽略。如果devicejype是LW_BCD,表示设备类型LW,数据的编码方式为BCD格 式。address.offset是PLC中的地址偏移量。例如,GetData(read_data_l O,t4FATEK FB Series,RT,5,1)代表读取的设备 地址偏移量为5o如果address_offset使用格式为N#AAAAA”,N表示PLC的站号,AAAAA 表示地址偏移量。此情况一般使用在同一个串口上连接有多台PLC或者控制 器的情况下。例如:GetData(read_data_l O,4tFATEK FB Series,RT,2#5,1)表 示读取站号为2的PLC的数据。如果GetData()使用“系统参数/设备列表”中 设定的默认的站号,在此可以不填这个站号。从PLC中读取的数据个数,根据read_data变量的类型和data_count的值来决 定的。如下表所示:read_data的类型data_count 的值读取16位数据的个数char(8-bit)1145char(8-bit)21bool(8-bit)11bool(8-bit)21short(16-bit)11short(16-bit)22int(32-bit)12int(32-bit)24float(32-bit)12float(32-bit)24当Getdata()函数读取32位的数据类型(int或者float型)时,此函数会自动的转换这个数据。例如:macro_command main()float fGetData(f,MODBUS,6x,2,1)/f中将会是浮点型的数据end macro_command举例macro_command main()bool abool b 30short cshort d 50int eint f 10double g 1046/读取LB2的状态到变量a中GetData(a,Local HMI,LB,2,1)/读取LBOLB29共30个状态,至I变量b 0b 29中GetData(b O,Local HMI“,LB,0,30)/读取LW2的数据到变量c中GetData(c,Local HMI,LW,2,1)/读取LWOLW49共50个字到变量d到d 49中GetData(d O,Local HMI,LW,0,50)/读取两个字LW6LW7到变量e中/注意此时变量e的类型为intGetData(e,Local HMF LW,6,1)/读取LWOLW19共20个字到变量ff 9中,数组f 10的变量类型定义为 intoGetData(f O,Local HMF,LW,0,10)/读取LW2LW3共2个字到变量f中GetData(f,Local HMI”,LW,2,1)end macro_command47函数名称SetData语法SetData(send_data start,device.函数名称,device_type,address_offset,data_count)orSetData(send_data,device.函数名称,device_type,address_offset,1)描述将数据写到 PLC 中。数据保存在 send_data startsend_data start+data_count-l 中。data_count是写入到PLC中数据的个数。一般来说,send_data是一个数组。但 是如果data_count是1,send_data可以是一个数组也可以是一个普通的变量。下面是写一个数据到PLC中的方法。macro_command main()short send_data_l 2=5,6,send_data_2=5SetData(send_data_l O,t4FATEK FB Series,RT,5,1)SetData(send_data_2,“FATEK FB Series,RT,5,1)end macro_commanddevice.函数名称详见上面的说明,在此不在说明。device_type是设备类型和PLC中数据的编码方式。例如:如果device_type是 LW_BIN,那么读取的设备类型为LW,数据编码方式为BINo如果使用BIN 编码方式,可以忽略。如果device_type是LW_BCD,表示设备类型LW,数据的编码方式为BCD格 式。48address.offset是PLC中的地址偏移量。例如,GetData(read_data_l O,TATEK FB Series,RT,5,1)代表读取的设备 地址偏移量为5o如果address_offset使用格式为“N#AAAAA”,N表示PLC的站号,AAAAA 表示地址偏移量。此情况一般使用在同一个串口上连接有多台PLC或者控制器 的情况下。例如:SetData(send_data_l O,“FATEKFB Series”,RT,2#5,1)表示 设定站号为2的PLC的数据。如果SetData。使用“系统参数/设备列表”中设 定的默认的站号,在此可以不填这个站号。设定到PLC的数据个数,根据sead_data变量的类型和data_count的值来决定 的。如下表所示:sead_data的类型data_count 的值设定16位数据的个数char(8-bit)11char(8-bit)21bool(8-bit)11bool(8-bit)21short(16-bit)11short(16-bit)22int(32-bit)12int(32-bit)24float(32-bit)12float(32-bit)2449当Setdata。函数写入32位的数据类型(int或者float型)到PLC时,此函数会自 动的转换这个数据。例如:macro_command main()float f=2.6SetData(f,MODBUS,6x,2,1)在此将会设定一个浮点数到PLC中end macro_command举例macro_command main()int ibool a=truebool b 30short c=falseshort d 50int e=5int f 10for i=0 to 29b i=truenext ifor i=0 to 49d i=i*2next ifor i=0 to 950f i=i*3next i 将变量a的数值设定到LB2中SetData(a,Local HMI”,LB,2,1)/设定LB0-LB29共30个位的状态SetData(b O,Local HMI,LB,0,30)/将变量c的值设定到LW2中SetData(c,Local HMI”,LW,2,1)/设定LWOLW49共50个数据SetData(d O,Local HMF,LW,0,50)/将变量e的值写入到LW6LW7两个寄存器中,注意变量e的类型为intoSetData(e,Local HMI”,LW,6,1)/设定LWOLW19共20个字的数据/10个双整型数据等于20个16位整型数。SetData(f O,40 and response l=0 x3)thenread_data O=response 4+(response 3 8)/计算4x_l 的数据read_data 1 =response 6+(response 5 8)/计算4x_2的数据63SetData(read_data O,Local HMI,LW,100,2)计算后的数据送到HMI上来显示 end if完整的宏指令程序如下:/Read Holding Registersmacro_command main()char command 32,response 32short address,checksumshort read_no,retum_value,read_data 2,iFILL(command 0,0,32)/initialize command 0-command 31 to 0FILL(response 0,0,32)command 0=0 x1/station numbercommand l=0 x3/read holding registers(function code is 0 x3)address=0address=0/starting address(4x_l)is 0HIBYTE(address,command 2)LOBYTE(address,command 3)read_no=2/the total words of reading is 2 wordsHIBYTE(read_no,command 4)LOB YTE(read_no,command 5)64CRC(command 0,checksum,6)/calculate 16-bit CRCLOBYTE(checksum,command 6)HIBYTE(checksum,command 7)OUTPORT(command 0,MODBUS RTU Device,8)/send requestINPORT(response 0,MODBUS RTU Device,9,retum_value)/read responseif(retum_value 0 and response l=0 x3)thenread_data O=response 4+(response 3 8)/4x_lread_data 1 =response 6+(response 5 8)/4x_2SetData(read_data O,Local HMI,LW,100,2)end ifend macro_command下面的举例说明如何使用自由协议设定MODBUS RTU设备中0 x_l的状态。这个是使用 MODBUS RTU协,议中的“写单个寄存器”的功能码“0 x05”来实现的。65发送命令Function code1 Byte0 x05Output Address2 Bytes0 x0000 to OxFFFFOutput Value2 Bytes0 x0000 or OxFFOO回且命令Function code1 Byte0 x05Output Address2 Bytes0 x0000 to OxFFFFOutput Value2 Bytes0 x0000 or OxFFOO错误代码Error code1 Byte0 x85Exception code1 Byte01 or 02 or 03 or 04完整的宏指令程序如下:/Write Single Coil(ON)macro_command main()char command 32,response 32short address,checksumshort i,retum_valueFILL(command 0,0,32)/initialize command()-command 31 to 0FILL(response 0,0,32)command 0=0 x1/station numbercommand l=0 x5/function code:write single coiladdress=0HIBYTE(address,command 2)LOBYTE(address,command 3)66command 4=Oxff/force Ox_l oncommand 5=0CRC(command 0,checksum,6)LOBYTE(checksum,command 6)HIBYTE(checksum,command 7)OUTPORT(command 0,MODBUS RTU Device,8)/send requestINPORT(response 0,MODBUS RTU Device,8,retum_value)/read response end macro_command30.9 编译错误提示信息1.错误信息格式:error c#:error 描述(#是错误信息编号)举例:error C37:undeclared identifier:i67当编译后提示有错误信息时,这个错误的描述内容可以参考错误信息编号。2.Error 描述(C1)语法error:“标识符”出现这个信息时,有许多种可能;举例:macro_command main()char i,123xyz/不支持这个变量类型end macro_command(C2)identifier5 used without having been initialized(使用的标识符没有初始化)宏指令必须定义声明的数组变量的大小For举例:macro_command main()char iint g i/i必须为一个数值常量end macro_command(C3)redefinition error:identifier(标识符被重复定义)函数名称和变量名称在有效范围内,必须是唯一的。For举例:68macro_command main()intg 10,g/重复定义错误end macro_command(C4)function函数名称error:,identifier,(函数名称定义错误)保留的关键字和常量,不能被定义为函数名称For举例:sub int if()/函数名称定义错误(C5)parentheses have not come in pairs(圆括号没有成对的出现)语句中丢失了(“或者”户举例:macro_command main)少了(C6)illegal expression without matching 气广(if 语句中没有合法的表达式)也就是在if语句中缺少表达式(C7)illegal expression(no then)without matching 4if?(if 语句中丢失了 then)也就是if和then没有匹配上(C8)illegal expression(no 4end if)(if 语句中缺少了 enf if)缺少了“end if”(C9)illegal nd iF without matching iF(end if 语句前缺少了 if 与其匹配)69End if语句前缺少了 if语句(CIO)illegaUelse(不合法的 else 语句)这个“if”语句的格式为:if 逻辑表达式thenelse if 逻辑表达式then end if任何与以上格式不符合的语句,在编译时就会报错。(C17)illegal expression(no,for)without matching next(没有与 next 相匹配的 for 语句)“for”语句错误:在“next”前,缺少了“for”语句(C18)illegal variable type(not integer or char)(不合法的变量类型)变量类型定义错误,此处应为整数型态或字符型态变量(C19)variable type error缺少赋值语句(C20)must be keyword 纯o,or Town,(缺少了关键词“to 或者down)缺少了关键词“to”或者“down”(C21)illegal expression(no next)(非法的表达式,缺少了“next”)70“for”语句的格式为:for 变量=初始值to 结束值stepnext 变量任何与上述格式不符合的语句,编译时都会报错。(C22)wend statement contains no whilewhile9循环不配对,wend,前面应有,while,关键词(C23)illegal expression without matching wend缺少“wend”关键词“while”语句的格式为:while 逻辑表达式wend任何不符合上述语法的,在编译时均会报错。(C24)语法 error:break不合法的break语句。break语句只能在for循环、while循环选择结构中使用,且break单独成一行。(C25)语法 error:Continue,71不合法的“continue”语句。Continue语句只会在“for”或者“while”语句中出现。(C26)语法 error表达式不正确(C27)语法 error表达式中缺少了一个运算符可能会造成这个编译错误信息。For举例:macro_command main()int a,bfor a=0 to 2b=4+xyz/不合法之处:xyz变量没有被定义next aend macro_command(C28)must be macro_command,此处应该为?macro_command,(C29)must be key word sub子函数的定义格式为:sub data type function_函数名称()end sub72For举例::sub int pow(int exp)end sub任何不符合上述语法结构的,在编译时均会报错。(C30)number of parameters is incorrect参数个数不对。(C31)parameter type is incorrect参数数据类型不匹配。调用函数时,参数必须在数据类型、个数上一一对应才能通过编 译,否则编译时将出现此项错误讯息。(C32)variable is incorrect变量类型不正确(C33)function 函数名称:undeclared function没有定义的函数名称(C34)expected constant expression不合法的数组下标表达形式73(C35)invalid array declaration不合法的数组定义(C36)array index error不合法的数组下标(C37)undeclared identifier:i identifier使用没有定义的变量。只能使用已经定义的变量和函数,否则编译时将出现此项错误 讯息。(C38)un-supported PLC data address通讯函数GetData()、SetData()的参数中有包含PLC地址类型信息,当PLC地址类 型不是此种PLC支持的地址类型时,编译时将出现此项错误讯息。(C39)idenifier must be integer,char or constant数组的格式为:声明:array_函数名称 constant(constant is the size of the array)使用:array_函数名称 integer,character or constant任何不符合上述规则的数组表达式,编译时将会报错(C40)execution 语法 should not exist before variable declaration or constant definition74变量定义语句的前面不能有执行语句For举例:macro_command main()int a,bfor a=0 To 2b=4+ainth,k/定义变量语句在此处是错误的,在一个函数内定义变量语句的前面不能有/执行语句,例如b=4+anext aend macro_command(C41)float variables cannot be contained in shift calculation移位运算中,操作数不能为浮点数。(C42)function must return a value函数应有返回值(C43)function should not return a value函数不应有返回值(C44)float variables cannot be contained in calculation运算中不能有float型资料75(C45)PLC address errorPLC地址错误(C46)array size overflow(max.4k)一维数组的大小超过4k(C47)macro command entry function is not only one宏指令程序入口只能有一个(C48)macro command entry function must be only one宏指令入口函数不唯一。宏指令的入口函数只能有一个,形式为:macro_command function函数名称()end macro_command(C49)an extended addressees station number must be between 0 and 255在宏指令中,扩展地址内的站号大小只能从0到255For举例:SetData(bits O,49+3=9+34+3=8+8=8b 4=not 8+1 and 2+1 or 0+1 xor 2b 5=
展开阅读全文