在CoDeSys中脚本语言的实现,提供了一个强大的CoDeSys序列。单命令或者复杂的命令序列,可以直接从CoDeSys中的编程环境或者通过窗口命令行中进行。在CoDeSys中脚本的模块化语言是基于 IronPython 2.7。为此CoDeSys的插件‘ScriptEngine’中集成了 IronPython解释器在CoDeSys环境中。该实现使用Python提供的外部的框架库去访问网络中的文件。
IronPython的脚本文件
Python是一种动态语言,用于各种广泛的用途,是一种强调清洁和表达性的代码。它能够为开发人员提供最大的灵活性,同时又保持代码的可读性。
IronPython 促使 Python 转到.NET,并允许本地访问 .NET 架构和类。实施 IronPyhon 的解释是基于Python 版本 2.7。
在网络上有很多免费的教程和帮助。以下链接会给你一个有关 IronPython的详细介绍及说明:
脚本命令
依据广泛的 Python 命令库大约有150 多个特殊的脚本命令在CoDeSys命令中进行处理。
|
有关脚本命令的帮助文档没有完成,并且没有放在CoDeSys在线帮助文件中。更多相关信息请参阅CoDeSys的“在线帮助”中的“ScriptEngine.chm”详细说明。
|
读 .NET API 文档理解 python 编程
目前初步版本的脚本文件是依靠 .NET / C# 代码自动生成的。因此它包含了一些 Python 程序员不常见的语法结构。下面的列表中给出了如何将它们转化成 python化的语言。
- .NET 中的接口是关于类(方法,属性)中必须包含执行接口的定义,在 IronPython中,一个接口可以通过派生它们作为一个基类的方式实现一个或者多个 .NET接口。当一个接口的成员没有定义时,一个异常将会在运行系统中被抛出。(DeviceImportFromSvn.py示例表示一个类实现了ImportReporter接口。)
- 在.NET中,所有的参数,属性和函数的返回值都是静态类型。在参数之前允许使用一些类型。对于函数来说,返回值的类型定义在函数名的前方。子类的实例允许被父类(或者接口)进行调用。
- 方法可以被重载,一个类中可以包含相同名称的多个方法,但是他们必须具有不同数量的参数类型和/或者数量。IronPython 将会自动调用最匹配的函数。
- 一个int型的变量可以包含一个 -2 147 483 648到2 147 483 647之间的数,bool变量等同于 python类型的 bool (True 和 False),类型string等同于python中的类型str,unicode(等同于 IronPython中的类型)。IDictionary<Object, Object>表示一个通常的python路径。IronPython会自动在 Python 和 .NET 类型间进行转化。
- 如果一个类型T来自IBaseObject<T>,该类型可以通过其他插件扩展更多的成员。在参数或者返回值中该类型T将会被标记为IExtendedObject<T>。
- 接口IEnumerable<T>描述了任意序列(列表,数组),这些类型序列都是类型T的对象(子对象)。当序列产生不兼容的对象,在运行系统中会抛出一个异常。
- 接口IList<T>描述了一个包含对象类型T(或者子对象)的列表。当添加一个不匹配的对象时,一个异常将会抛出。
- 语法params T [] name等同于 python 中的语法 *name中的可变参数列表,但是对输入参数T(或者子类)有限制。
- 枚举变量 (enum) 在 python中不是作为一种语言结构。他们用于定义一定数量的常量,例如,一周的天数。定义在 .NET中的枚举变量可以在 IronPython 中通过名称.Member 的语法结构进行调用(类似于类成员的调用)例如 OnlineChangeOption.Try。 在 python中有多种枚举形式,比如http://pypi.python.org/pypi/enum/或者http://www.ironpython.info/index.php/Enumerations
- 标记在{ get; set; }中的属性是读写变量,标记在{ get; }中的属性是只读类型。类似于python中@property的定义。
对于脚本文件以下切入点是可以允许的:
-
system:在CoDeSys系统中整合的函数。这个对象提供了所有描述在 "ISystem Interface"下的函数,例如,CoDeSys的退出,消息窗口的访问或者通过使用ui-present命令运行的 --noUI 模式。
-
projects:工程管理的基本函数。这些对象提供了多有在 "IScriptProjects Interface"中描述的函数,例如,加载项目及项目档案。此外,它也是个别项目的入口点。
-
online:在线连接设备的基本函数。通过使用"create_online_application" 对一个应用对象的在线对象进行管理。这个在线对象允许登录到控制器,启动应用程序并且初始化变量的值。
接口点的相信说明
对象
|
名称(类型)
|
描述
|
系统
|
system (ISystem)
|
CoDeSys系统基本函数
|
|
Severity
|
消息优先级的枚举
|
|
Guid
|
数据类型 'g全局变量 u联合变量 Id识别
|
|
PromptResult
|
用户需求的返回变量的枚举
|
|
MultipleChoiceSelector
|
选择类型提示
|
|
PromptChoiceFilter
|
选择文件类型提示
|
工程
|
projects (IScriptProjects)
|
工程管理的基本功能
|
|
ExportReporter
|
导出过程中的事件接口
|
|
ImportReporter
|
导入过程中的事件接口
|
|
ConflictResolve
|
导入过程中冲突的枚举
|
在线
|
online (IScriptOnline)
|
在线连接设备的基本功能
|
|
OnlineChangeOption
|
登录到设备下载类型的枚举
|
|
ApplicationState
|
应用状态的枚举
|
|
OperatingState
|
操作模式的枚举
|
|
ValuesFailedException
|
在线异常表达错误
|
|
TimeoutException
|
在线操作异常
|
设备对象
|
DeviceID
|
类型封装设备识别
|
脚本示例1:读取变量
在以下示例列表中可以从CoDeSys的用户接口或者命令行启动。
要启动它,需要改变CoDeSys的安装路径(<installation path of CoDeSys>\CoDeSys\Common)并且输入命令start /wait CoDeSys.exe --runscript="D:\data\scripts\ReadVariable.py"
脚本打开了CoDeSys,并且登录设备。如果控制器不是在“运行”模式将会设置为“运行”。然后变量iVar1 被读取并且显示在消息窗口或者命令行。最后应用程序被关闭。
# 脚本示例 ReadVariable.py
# Close all projects
while len(projects.all) > 0:
projects.all[0].close()
# opens project
proj = projects.open("D:\\data\\projects\\Ampel.project")
# set "Ampel.project" to active application
app = proj.active_application
onlineapp = online.create_online_application(app)
# login to device
onlineapp.login(OnlineChangeOption.Try, True)
# set status of application to "run", if not in "run"
if not onlineapp.application_state == ApplicationState.run:
onlineapp.start()
# wait 1 second
system.delay(1000)
# read value of iVar1
value = onlineapp.read_value("PLC_PRG.iVar1")
# display value in message view or command line
print value
# log out from device and close "Ampel.project"
onlineapp.logout()
proj.close()
脚本示例2: 从配方中读取值并发送邮件
在下面的示例列表中可以通过CoDeSys用户接口或者命令行开始。
要启动它,首先在命令行中输入CoDeSys版本中 "Common" 的路径(<安装路径 CoDeSys>\CoDeSys\Common) 并且输入命令start /wait CoDeSys.exe --runscript="D:\data\scripts\ScriptEmail.py"
脚本打开CoDeSys的一个应用并且登录设备。如果控制器没有在“运行”状态将会被设置为“运行”。然后变量 iVar1 的值将会被读取并且显示在消息窗口或者命令行。最后应用程序被关闭。
#脚本示例ScriptEmail.py
# Close current project if necessary and open "ScriptTest.project"
if not projects.primary == None:
projects.primary.close()
project = projects.open("D:\\Data\\projects\\scriptTest.project")
# retrieve active application
application = project.active_application
# create online application
online_application = online.create_online_application(application)
# login to application.
online_application.login(OnlineChangeOption.Try, True)
# start PLC if necessary
if not online_application.application_state == ApplicationState.run:
online_application.start()
# wait 2 seconds
system.delay(2000)
# open recipe file to read values.
recipe_input_file = open("D:\\Data\\projects\\RecipeInput.txt", "r")
watch_expressions = []
for watch_expression in recipe_input_file:
watch_expressions.append(watch_expression.strip())
print watch_expressions
# read values from the controllerd
watch_values = online_application.read_values(watch_expressions)
print watch_values
# open output file to write values
recipe_output_file = open("D:\\Data\\projects\\RecipeOutput.txt", "w")
for i in range(len(watch_expressions)):
recipe_output_file.write(watch_expressions[i])
recipe_output_file.write(" = ")
recipe_output_file.write(watch_values[i])
recipe_output_file.write("\n")
# Close files
recipe_input_file.close()
recipe_output_file.close()
# send Email
# import respective libraries
import smtplib
from email.mime.text import MIMEText
#open output file
recipe_output_file = open("D:\\Data\\projects\\RecipeOutput.txt", "r")
mail = MIMEText(recipe_output_file.read())
recipe_output_file.close()
#email address sender and recipient
fromm = "info@3s-software.com"
to = "info@3s-software.com"
# set sender and recipient
mail["Subject"] = "Attention value has changed"
mail["From"] = fromm
mail["To"] = to
# send email
smtp = smtplib.SMTP("name of smtp server")
smtp.sendmail(fromm, [to], mail.as_string())
smtp.quit()
# logout and close application
online_application.logout()
project.close()
脚本示例 3: 确定工程中的设备树中打开项目
此示例用于确定打开项目中设备树中的所有对象,并显示在命令行或者消息视图中。可以通过示例1描述的方法启动。
# 脚本示例 DevicePrintTree.py
# We enable the new python 3 print syntax
from __future__ import print_function
import sys
# define the printing function
def printtree(treeobj, depth=0):
if treeobj.is_root:
name = treeobj.path
deviceid = ""
else:
name = treeobj.get_name(False)
if treeobj.is_device:
deviceid = treeobj.get_device_identification()
else:
deviceid = ""
print("{0} - {1} {2}".format(" "*depth, name, deviceid))
for child in treeobj.get_children(False):
printtree(child, depth+1)
# Now see whether a primary project is open.
if not projects.primary:
print("Error: Please open a project file first!", file=sys.stderr)
sys.exit()
# And the actual output
print("--- The current tree of devices: ---")
printtree(projects.primary)
print("--- Script finished. ---")
脚本示例 4: 从子版本中导入设备 PLCOpenXML 文件
示例展示从一个子版本中通过SVN端读取设备的PLCOpenXML文件。可以通过示例1描述的启动方式进行启动。
# 脚本示例 DeviceImportFromSvn.py
# Imports a Device in PLCOpenXML from Subversion via command line svn client.
# We enable the new python 3 print syntax
from __future__ import print_function
import sys, os
# some variable definitions:
SVNEXE = r"C:\Program Files\Subversion\bin\svn.exe"
XMLURL = "file:///D:/testrepo/testfolder/TestExport.xml"
PROJECT = r"D:\test.project"
# clean up any open project:
if projects.primary:
projects.primary.close()
# Fetch the plcopenxml data from subversion.
# The 'with' construct automatically closes the open pipe for us.
with os.popen('"' + SVNEXE + '" cat ' + XMLURL, 'r') as pipe:
xmldata = pipe.read()
# create a new project:
proj = projects.create(PROJECT)
# create the import reporter
class Reporter(ImportReporter):
def error(self, message):
system.write_message(Severity.Error, message)
def warning(self, message):
system.write_message(Severity.Warning, message)
def resolve_conflict(self, obj):
return ConflictResolve.Copy
def added(self, obj):
print("added: ", obj)
def replaced(self, obj):
print("replaced: ", obj)
def skipped(self, obj):
print("skipped: ", obj)
@property
def aborting(self):
return False
# create the importer instance.
reporter = Reporter()
# import the data into the project.
proj.import_xml(reporter, xmldata)
# and finally save. :-)
proj.save()
print("--- Script finished. ---")