Skip to main content

使用脚本访问 CODESYS 功能

所有对象和命令 CODESYS 提供脚本编写也可在“scriptengine" Python 模块。每当一个脚本启动时,一个隐式的 <code>from scriptengine import *</code> 结果。这允许轻松访问 CODESYS.但是,如果您的脚本导入需要访问的模块 CODESYS API,然后这些模块必须导入模块 scriptengine 他们自己。

在 Python 脚本中可用作入口点的主要对象(类别)如下表所示。入口点的详细文档可以在 API 参考文档CODESYS 脚本引擎。

对象

描述

系统

访问一般 CODESYS 功能

例子:

  • 退出 CODESYS

  • 处理一般用户界面

  • 访问消息存储器(包括编译器消息)

  • 控制延迟和进度条

项目

访问 CODESYS 项目作为对象树,将三个导航器视图(设备、POU、模块)组合在一个项目树中

还允许加载、创建、保存和关闭项目

对于项目中的大多数对象,都有具有详细功能的特殊方法,例如编译、访问 ST POU、导出、导入、设备配置等。

在线的

访问在线功能

例子:

  • 登录设备和应用程序

  • 访问数据管理(用户名、密码)

  • 网络扫描的性能

  • 网关管理

图书馆管理员

允许管理库存储库以及查看、安装和删除库

device_repository

设备存储库的处理;设备描述的导入和导出

模块库

管理 CODESYS Application Composer 模块和 CODESYS Application Composer 存储库

有关访问的特定示例脚本,请参见下文 CODESYS- 功能。有关详细信息,请参阅 API 参考文档CODESYS.

示例:打印当前项目的设备树

剧本 PrintDeviceTree.py 是在项目中导航的示例。它创建打开项目中所有设备的分层显示的输出。

加载一个包含一些设备对象的项目并执行脚本。

22. 例子: 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. ---")

设备树(来自“设备”视图)显示在消息视图中,所有非设备对象都被排除在外:

_cds_script_messages_print_device_tree.png


示例:读取变量

剧本 ReadVariable.py 登录设备并在必要时启动应用程序。然后是变量的值 PLC_PRG.iVar1 被读取和输出。要尝试该脚本,您必须修改项目路径和变量名称。

23. 例子: 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 服务器的路径、电子邮件地址和名称。

24. 例子: 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 已经必须在场。

25. 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 显示了这方面的所有可能功能。

26. 例子: 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!")

print("Username and passwort prompts...")
res = system.ui.query_password("Please enter your favourite password!", cancellable=True)
if res:
        print("Huh, it's very careless to tell me your favourite password '%s'!" % res)
else:
    print("Ok, if you don't want...")

res = system.ui.query_credentials("Now, for real...")
if res:
        print("Username '%s' and password '%s'" % res) # res is a 2-tuple
else:
    print("Sigh...")


示例:操作 项目信息 目的

在脚本中 ProjectInfoExample.py,我们在 项目信息 目的。最重要的信息项,例如 标题版本, 具有显式性质。但是,您可以通过 dictionary 句法。例如,推荐用于库项目属性的那些。

下面的示例可能看起来有些不切实际,但类似的代码用于创建、测试和可能发布自动库项目和其他项目的构建服务器。 ScriptEngine 是创建 CI(持续集成)和 CD(持续交付)系统的关键元素之一。

27. 例子: 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 项目。

要使用该脚本,您必须修改环境的路径。

28. 例子: 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. ---")


高级示例:从 SVN 调用库并将其安装在 CODESYS

以下示例脚本可以作为 CT(持续测试)环境的一部分执行库的调用和安装,以便对其进行测试。除了标准——CODESYS, 这 CODESYS SVN 附加组件也必须使用有效的许可证安装。

29. 例子
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()