使用脚本访问CODESYS功能
所有对象和命令CODESYS提供的脚本也可在“scriptengine " Python 模块。每当脚本启动时,隐式<code>from scriptengine import *</code>结果。这样可以轻松访问CODESYS。但是,如果您的脚本导入需要访问的模块CODESYS API,那么这些模块必须导入模块scriptengine他们自己。
下表列出了 Python 脚本中可用作入口点的主要对象(类别)。有关入口点的完整文档,请参阅 API 参考文档CODESYS脚本引擎: CODESYS 脚本 API。
对象 | 描述 |
|---|---|
system | 访问一般CODESYS功能 例子:
|
projects | 访问CODESYS项目作为对象树,将三个导航器视图(设备、POU、模块)组合在一个项目树中 还允许加载、创建、保存和关闭项目 对于项目中的大多数对象,都有具有详细功能的特殊方法,例如编译、访问 ST POU、导出、导入、设备配置等。 |
online | 访问在线功能 例子:
|
librarymanager | 允许管理库存储库以及查看、安装和删除库 |
device_repository | 处理设备存储库;导入和导出设备描述 |
modulerepository | 管理CODESYS Application Composer模块和CODESYS Application Composer存储库 |
请参阅以下具体示例脚本,了解访问方法CODESYS功能。有关详细信息,请参阅CODESYS脚本引擎: CODESYS 脚本 API。
示例:打印当前项目的设备树
脚本PrintDeviceTree.py是项目导航的示例。它创建了打开项目中所有设备的分层显示输出。
加载包含一些设备对象的项目并执行脚本。
PrintDeviceTree.py# encoding:utf-8
# We enable the new python 3 print syntax
from __future__ import print_function
# Prints out all devices in the currently open project.
print("--- Printing the devices of the project: ---")
# Define the printing function. This function starts with the
# so called "docstring" which is the recommended way to document
# functions in python.
def print_tree(treeobj, depth=0):
""" Print a device and all its children
Arguments:
treeobj -- the object to print
depth -- The current depth within the tree (default 0).
The argument 'depth' is used by recursive call and
should not be supplied by the user.
"""
# if the current object is a device, we print the name and device identification.
if treeobj.is_device:
name = treeobj.get_name(False)
deviceid = treeobj.get_device_identification()
print("{0}- {1} {2}".format("--"*depth, name, deviceid))
# we recursively call the print_tree function for the child objects.
for child in treeobj.get_children(False):
print_tree(child, depth+1)
# We iterate over all top level objects and call the print_tree function for them.
for obj in projects.primary.get_children():
print_tree(obj)
print("--- Script finished. ---")设备树(来自“设备”视图)显示在消息视图中,并且所有非设备对象都被忽略:

示例:读取变量
脚本ReadVariable.py登录设备并在必要时启动应用程序。然后变量的值PLC_PRG.iVar1读取并输出。要尝试该脚本,您需要修改项目路径和变量名称。
ReadVariable.py# encoding:utf-8
from __future__ import print_function
# close open project if necessary:
if projects.primary:
projects.primary.close()
# opens project
proj = projects.open(r"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()在脚本的扩展中ReadVariable.py,脚本MailVariables.py从配方文件加载变量和表达式,并从控制器读取它们的当前值。然后,这些值被写回到同一个文件。此外,它使用 Python SMTP 库发送一封带有附件的电子邮件,附件中包含所有变量的列表。
要使用该脚本,您需要修改路径、电子邮件地址和 SMTP 服务器的名称以适合您的环境。
MailVariables.py# encoding:utf-8
from __future__ import print_function
# 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@example.com"
to = "info@example.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()示例:创建和编辑 POU
脚本CreateDut.py创建对象MyStruct,MyAlias, 和MyUnion在CODESYS项目。文件夹DataTypes已经存在。
CreateDut.py# encoding:utf-8
from __future__ import print_function
STRUCT_CONTENT = """\
a : BOOL;
b : BIT;
c : BIT;
"""
UNION_WHOLE = """\
TYPE MyUnion :
UNION
Zahl : INT;
Prozent : MyAlias;
Bits : MyStruct;
END_UNION
END_TYPE
"""
proj = projects.primary
folder = proj.find('DataTypes', recursive = True)[0]
# Create a struct DUT and insert the list of variables just into the right
# place in line two, row 0 (line numbering starts with line 0)
struktur = folder.create_dut('MyStruct') # DutType.Structure is the default
struktur.textual_declaration.insert(2, 0, STRUCT_CONTENT)
# Alias types get their "content" via the base type, which will just end up
# as one line in the declaration part:
# TYPE MyAlias : INT (0..100); END_TYPE
bereich = folder.create_dut('MyAlias', DutType.Alias, "INT (0..100)")
# Instead of injecting the variables into the existing declaration,
# one can also just replace the complete declaration part, including the
# boilerplate code.
union = folder.create_dut('MyUnion', DutType.Union)
union.textual_declaration.replace(UNION_WHOLE)示例:用户界面/与用户的交互
在某些情况下,脚本必须与用户交互。我们为最常见的交互提供了一些简单的 API。示例脚本System_UI_Test.py展示了这方面的所有可能的功能。
System_UI_Test.py# encoding:utf-8
from __future__ import print_function
"""Performs some tests on the messagestore and UI."""
print("Some Error, Warning and Information popups:")
system.ui.error("Fatal error: Everything is OK. :-)")
system.ui.warning("Your bank account is surprisingly low")
system.ui.info("Just for your information: 42")
print("Now, we ask the user something.")
res = system.ui.prompt("Do you like this?", PromptChoice.YesNo, PromptResult.Yes);
print("The user selected '%s'" % res)
print("Now, the user can choose between custom options:")
res = system.ui.choose("Please choose:", ("First", 2, 7.5, "Something else"))
print("The user selected option '%s'" % str(res)) # res is a tuple
print("Now, the user can choose several options:")
res = system.ui.select_many("Please select one or more options", PromptChoice.OKCancel, PromptResult.OK, ("La Premiere", "The Second", "Das Dritte"))
print("The returned result is: '%s'" % str(res)) # res is a tuple
print("Now, the user can select files and directories")
res = system.ui.open_file_dialog("Choose multiple files:", filter="Text files (*.txt)|*.txt|Image Files(*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF|All files (*.*)|*.*", filter_index = 0, multiselect=True)
print("The user did choose: '%s'" % str(res)) # res is a tuple as multiselect is true.
res = system.ui.save_file_dialog("Choose a file to save:", filter="Text files (*.txt)|*.txt|Image Files(*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF|All files (*.*)|*.*", filter_index = 0)
print("The user did choose: '%s'" % res)
res = system.ui.browse_directory_dialog("Choose a directory", path="C:\\")
print("The user did choose: '%s'" % res)
print("Now we query a single line string")
res = system.ui.query_string("What's your name?")
print("Nice to meet you, dear %s." % res)
print("Now we query a multi line string")
res = system.ui.query_string("Please tell me a nice story about your life!", multi_line=True)
if (res):
print("Huh, that has been a long text, at least %s characters!" % len(res))
else:
print("Hey, don't be lazy!")
示例:操作项目信息对象
在脚本中ProjectInfoExample.py,我们在项目信息对象。最重要的信息项,例如标题和版本,具有显式属性。但是,您可以通过dictionary语法。例如,推荐用于库项目属性的语法。
下面的示例可能看起来有些不切实际,但在构建服务器中也使用了类似的代码,用于创建、测试并可能发布自动化库项目和其他项目。ScriptEngine 是创建 CI(持续集成)和 CD(持续交付)系统的关键元素之一。
ProjectInfoExample.py# encoding:utf-8
from __future__ import print_function
proj = projects.load("D:\Some.library")
info = proj.get_project_info()
# Set some values
info.company = "Test Library Ltd"
info.title = "Script Test Project"
info.version = (0, 8, 15, 4711)
info.default_namespace = "testlibrary"
info.author = "Python von Scriptinger"
# some values recommended in the library toolchain
info.values["DefaultNamespace"] = "testlibrary"
info.values["Placeholder"] = "testlibrary"
info.values["DocFormat"] = "reStructuredText"
# now we set a custom / vendor specific value.
info.values["SpecialDeviceId"] = "PLC0815_4711"
# Enable generation of Accessor functions, so the IEC
# application can display the version in an info screen.
info.change_accessor_generation(True)
# And set the library to released
info.released = True;
proj.save()示例:调用外部命令并导入 PLCopenXML 文件
示例脚本DeviceImportFromSVN.py从外部程序(在本例中为 SVN 客户端)获取 PLCopenXML 文件并将其导入到新创建的CODESYS项目。
要使用该脚本,您需要修改环境的路径。
DeviceImportFromSVN.py# encoding:utf-8
# 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.
# We'll catch the output of the program into the xmldata variable.
# 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)
# import the data into the project.
proj.import_xml(xmldata, False)
# and finally save. :-)
proj.save()
print("--- Script finished. ---")示例:项目归档
功能open_archive()和save_archive()是用于提取项目存档或将项目另存为项目存档的脚本函数。对于这两个函数,ArchiveCategories可以指定。
为了这,
open_archive()接受参数categories_to_extract。save_archive()接受参数additional_categories。
ArchiveCategories是项目档案类别,在以下情况下与项目档案一起提取open_archive()或与项目档案一起归档,如果save_archive()。
例子:open_archive()
open_archive() 的各种示例,带有参数categories_to_extract介绍如下:
默认行为:categories_to_extract=None
如果categories_to_extract没有明确指定,则参数设置为None . 所有类别均从项目档案中拉出并提取。
# Default projects.open_archive(stArchiveFile=r"D:\Tests\Test.projectarchive", stProjectPath=r"D:\Tests", bOverwrite=True)
空列表:categories_to_extract=[]
如果参数categories_to_extract明确指定了一个空列表,那么就不会从项目档案中提取任何类别。
# Passing empty list projects.open_archive(stArchiveFile=r"D:\Tests\Test.projectarchive", stProjectPath=r"D:\Tests", bOverwrite=True, categories_to_extract=[])
多个类别
参数中可以指定多个类别categories_to_extract。也仅提取指定的类别。
# Multiple categories projects.open_archive(stArchiveFile=r"D:\Tests\Test.projectarchive", stProjectPath=r"D:\Tests", bOverwrite=True, categories_to_extract=[ArchiveCategories.libraries, ArchiveCategories.devices])
可能的错误
如果在categories_to_extract目前不存在的CODESYS安装时,会显示错误消息。无法提取项目档案,除非安装缺少的类别或从脚本中的参数中删除该类别。
例子:save_archive()
各种例子save_archive()使用参数additional_categories介绍如下:
默认行为
如果additional_categories如果没有明确设置,则默认类别CODESYS界面也已存档。
没有类别:additional_categories=None
如果additional_categories明确设置为None在脚本中,则不会存档任何类别。
多个类别
参数中可以指定多个类别additional_categories。只有指定的类别才会被包含在档案中。
# Muliple categories projects.save_archive(path=r"D:\Tests", comment="A new project", additional_files=[], additional_categories=[ArchiveCategories.libraries, ArchiveCategories.devices])
高级示例:从 SVN 调用库并将其安装在 CODESYS 中
以下示例脚本可以在 CT(持续测试)环境中执行库的调用和安装,以便对其进行测试。除了标准之外,CODESYS, 这CODESYS SVN附加组件还必须使用有效许可证进行安装。
import tempfile
if projects.primary:
projects.primary.close()
tempdir = tempfile.mkdtemp()
URL = "svn://localhost/testrepo/trunk/SvnTestLibrary/"
proj = svn.checkout(URL, tempdir, "testlibrary", as_library=True)
proj.save()
repo = librarymanager.repositories[0]
librarymanager.install_library(proj.path, repo, True)
proj.close()